diff options
author | Julian Eisel <julian@blender.org> | 2021-06-05 18:51:46 +0300 |
---|---|---|
committer | Julian Eisel <julian@blender.org> | 2021-06-05 18:51:46 +0300 |
commit | 87a821397fa4db7b72ea809ee29c319cf4788a1a (patch) | |
tree | 5b14c3657585b7e64ce1a3e14492925e0a79f473 | |
parent | d6e9b0ce5d61f64a3879459c9d39a151aabdea81 (diff) | |
parent | edaaa2afddb2132e56f39791e559b084b6df8773 (diff) |
Merge branch 'master' into asset-system-filelistasset-system-filelist
608 files changed, 17367 insertions, 12310 deletions
diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake index cd693d766dc..b1d5dff91a0 100644 --- a/build_files/build_environment/cmake/embree.cmake +++ b/build_files/build_environment/cmake/embree.cmake @@ -43,6 +43,12 @@ endif() if(WIN32) set(EMBREE_BUILD_DIR ${BUILD_MODE}/) + if(BUILD_MODE STREQUAL Debug) + list(APPEND EMBREE_EXTRA_ARGS + -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_debug + -DEMBREE_TBB_LIBRARY_NAME=tbb_debug + ) + endif() else() set(EMBREE_BUILD_DIR) endif() diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake index 44307cd2afb..005616a9f8d 100644 --- a/build_files/build_environment/cmake/tbb.cmake +++ b/build_files/build_environment/cmake/tbb.cmake @@ -22,6 +22,7 @@ if(WIN32) -DTBB_BUILD_TBBMALLOC_PROXY=On -DTBB_BUILD_STATIC=Off -DTBB_BUILD_TESTS=Off + -DCMAKE_DEBUG_POSTFIX=_debug ) set(TBB_LIBRARY tbb) set(TBB_STATIC_LIBRARY Off) @@ -55,17 +56,17 @@ if(WIN32) ExternalProject_Add_Step(external_tbb after_install # findtbb.cmake in some deps *NEEDS* to find tbb_debug.lib even if they are not going to use it # to make that test pass, we place a copy with the right name in the lib folder. - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb_debug.dll - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${LIBDIR}/tbb/lib/tbb_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb.dll ${LIBDIR}/tbb/bin/tbb_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc.dll ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll # Normal collection of build artifacts COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb.dll ${HARVEST_TARGET}/tbb/bin/tbb.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy.dll COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/tbb/include/ ${HARVEST_TARGET}/tbb/include/ DEPENDEES install ) @@ -76,11 +77,11 @@ if(WIN32) # to make that test pass, we place a copy with the right name in the lib folder. COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${LIBDIR}/tbb/lib/tbb.lib # Normal collection of build artifacts - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.dll ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.dll - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc.dll - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc_proxy.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb_debug.dll ${HARVEST_TARGET}/tbb/bin/tbb_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy_debug.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy_debug.dll DEPENDEES install ) endif() diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 83e438614b6..4500245d42a 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -43,7 +43,7 @@ set(JPEG_FILE libjpeg-turbo-${JPEG_VERSION}.tar.gz) set(BOOST_VERSION 1.73.0) set(BOOST_VERSION_NODOTS 1_73_0) set(BOOST_VERSION_NODOTS_SHORT 1_73) -set(BOOST_URI https://dl.bintray.com/boostorg/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz) +set(BOOST_URI https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz) set(BOOST_HASH 4036cd27ef7548b8d29c30ea10956196) set(BOOST_HASH_TYPE MD5) set(BOOST_FILE boost_${BOOST_VERSION_NODOTS}.tar.gz) @@ -297,10 +297,10 @@ set(OPENJPEG_HASH 63f5a4713ecafc86de51bfad89cc07bb788e9bba24ebbf0c4ca637621aadb6 set(OPENJPEG_HASH_TYPE SHA256) set(OPENJPEG_FILE openjpeg-v${OPENJPEG_VERSION}.tar.gz) -set(FFMPEG_VERSION 4.2.3) +set(FFMPEG_VERSION 4.4) set(FFMPEG_URI http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2) -set(FFMPEG_HASH 695fad11f3baf27784e24cb0e977b65a) -set(FFMPEG_HASH_TYPE MD5) +set(FFMPEG_HASH 42093549751b582cf0f338a21a3664f52e0a9fbe0d238d3c992005e493607d0e) +set(FFMPEG_HASH_TYPE SHA256) set(FFMPEG_FILE ffmpeg-${FFMPEG_VERSION}.tar.bz2) set(FFTW_VERSION 3.3.8) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 18922799d08..71ddeaa4576 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -563,9 +563,9 @@ OIDN_SKIP=false ISPC_VERSION="1.14.1" -FFMPEG_VERSION="4.2.3" -FFMPEG_VERSION_SHORT="4.2" -FFMPEG_VERSION_MIN="3.0" +FFMPEG_VERSION="4.4" +FFMPEG_VERSION_SHORT="4.4" +FFMPEG_VERSION_MIN="4.4" FFMPEG_VERSION_MAX="5.0" FFMPEG_FORCE_BUILD=false FFMPEG_FORCE_REBUILD=false diff --git a/build_files/buildbot/README.md b/build_files/buildbot/README.md index 06733c9a42d..f6fd07d9246 100644 --- a/build_files/buildbot/README.md +++ b/build_files/buildbot/README.md @@ -1,70 +1,4 @@ -Blender Buildbot -================ +Buildbot Configuration +===================== -Code signing ------------- - -Code signing is done as part of INSTALL target, which makes it possible to sign -files which are aimed into a bundle and coming from a non-signed source (such as -libraries SVN). - -This is achieved by specifying `worker_codesign.cmake` as a post-install script -run by CMake. This CMake script simply involves an utility script written in -Python which takes care of an actual signing. - -### Configuration - -Client configuration doesn't need anything special, other than variable -`SHARED_STORAGE_DIR` pointing to a location which is watched by a server. -This is done in `config_builder.py` file and is stored in Git (which makes it -possible to have almost zero-configuration buildbot machines). - -Server configuration requires copying `config_server_template.py` under the -name of `config_server.py` and tweaking values, which are platform-specific. - -#### Windows configuration - -There are two things which are needed on Windows in order to have code signing -to work: - -- `TIMESTAMP_AUTHORITY_URL` which is most likely set http://timestamp.digicert.com -- `CERTIFICATE_FILEPATH` which is a full file path to a PKCS #12 key (.pfx). - -## Tips - -### Self-signed certificate on Windows - -It is easiest to test configuration using self-signed certificate. - -The certificate manipulation utilities are coming with Windows SDK. -Unfortunately, they are not added to PATH. Here is an example of how to make -sure they are easily available: - -``` -set PATH=C:\Program Files (x86)\Windows Kits\10\App Certification Kit;%PATH% -set PATH=C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64;%PATH% -``` - -Generate CA: - -``` -makecert -r -pe -n "CN=Blender Test CA" -ss CA -sr CurrentUser -a sha256 ^ - -cy authority -sky signature -sv BlenderTestCA.pvk BlenderTestCA.cer -``` - -Import the generated CA: - -``` -certutil -user -addstore Root BlenderTestCA.cer -``` - -Create self-signed certificate and pack it into PKCS #12: - -``` -makecert -pe -n "CN=Blender Test SPC" -a sha256 -cy end ^ - -sky signature ^ - -ic BlenderTestCA.cer -iv BlenderTestCA.pvk ^ - -sv BlenderTestSPC.pvk BlenderTestSPC.cer - -pvk2pfx -pvk BlenderTestSPC.pvk -spc BlenderTestSPC.cer -pfx BlenderTestSPC.pfx -```
\ No newline at end of file +Files used by Buildbot's `compile-code` step. diff --git a/build_files/buildbot/buildbot_utils.py b/build_files/buildbot/buildbot_utils.py deleted file mode 100644 index 7e9858d9268..00000000000 --- a/build_files/buildbot/buildbot_utils.py +++ /dev/null @@ -1,127 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import argparse -import os -import re -import subprocess -import sys - - -def is_tool(name): - """Check whether `name` is on PATH and marked as executable.""" - - # from whichcraft import which - from shutil import which - - return which(name) is not None - - -class Builder: - def __init__(self, name, branch, codesign): - self.name = name - self.branch = branch - self.is_release_branch = re.match("^blender-v(.*)-release$", branch) is not None - self.codesign = codesign - - # Buildbot runs from build/ directory - self.blender_dir = os.path.abspath(os.path.join('..', 'blender.git')) - self.build_dir = os.path.abspath(os.path.join('..', 'build')) - self.install_dir = os.path.abspath(os.path.join('..', 'install')) - self.upload_dir = os.path.abspath(os.path.join('..', 'install')) - - # Detect platform - if name.startswith('mac'): - self.platform = 'mac' - self.command_prefix = [] - elif name.startswith('linux'): - self.platform = 'linux' - if is_tool('scl'): - self.command_prefix = ['scl', 'enable', 'devtoolset-9', '--'] - else: - self.command_prefix = [] - elif name.startswith('win'): - self.platform = 'win' - self.command_prefix = [] - else: - raise ValueError('Unkonw platform for builder ' + self.platform) - - # Always 64 bit now - self.bits = 64 - - -def create_builder_from_arguments(): - parser = argparse.ArgumentParser() - parser.add_argument('builder_name') - parser.add_argument('branch', default='master', nargs='?') - parser.add_argument("--codesign", action="store_true") - args = parser.parse_args() - return Builder(args.builder_name, args.branch, args.codesign) - - -class VersionInfo: - def __init__(self, builder): - # Get version information - buildinfo_h = os.path.join(builder.build_dir, "source", "creator", "buildinfo.h") - blender_h = os.path.join(builder.blender_dir, "source", "blender", "blenkernel", "BKE_blender_version.h") - - version_number = int(self._parse_header_file(blender_h, 'BLENDER_VERSION')) - version_number_patch = int(self._parse_header_file(blender_h, 'BLENDER_VERSION_PATCH')) - version_numbers = (version_number // 100, version_number % 100, version_number_patch) - self.short_version = "%d.%d" % (version_numbers[0], version_numbers[1]) - self.version = "%d.%d.%d" % version_numbers - self.version_cycle = self._parse_header_file(blender_h, 'BLENDER_VERSION_CYCLE') - self.hash = self._parse_header_file(buildinfo_h, 'BUILD_HASH')[1:-1] - - if self.version_cycle == "release": - # Final release - self.full_version = self.version - self.is_development_build = False - elif self.version_cycle == "rc": - # Release candidate - self.full_version = self.version + self.version_cycle - self.is_development_build = False - else: - # Development build - self.full_version = self.version + '-' + self.hash - self.is_development_build = True - - def _parse_header_file(self, filename, define): - import re - regex = re.compile(r"^#\s*define\s+%s\s+(.*)" % define) - with open(filename, "r") as file: - for l in file: - match = regex.match(l) - if match: - return match.group(1) - return None - - -def call(cmd, env=None, exit_on_error=True): - print(' '.join(cmd)) - - # Flush to ensure correct order output on Windows. - sys.stdout.flush() - sys.stderr.flush() - - retcode = subprocess.call(cmd, env=env) - if exit_on_error and retcode != 0: - sys.exit(retcode) - return retcode diff --git a/build_files/buildbot/codesign/absolute_and_relative_filename.py b/build_files/buildbot/codesign/absolute_and_relative_filename.py deleted file mode 100644 index cb42710e785..00000000000 --- a/build_files/buildbot/codesign/absolute_and_relative_filename.py +++ /dev/null @@ -1,81 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -from dataclasses import dataclass -from pathlib import Path -from typing import List - - -@dataclass -class AbsoluteAndRelativeFileName: - """ - Helper class which keeps track of absolute file path for a direct access and - corresponding relative path against given base. - - The relative part is used to construct a file name within an archive which - contains files which are to be signed or which has been signed already - (depending on whether the archive is addressed to signing server or back - to the buildbot worker). - """ - - # Base directory which is where relative_filepath is relative to. - base_dir: Path - - # Full absolute path of the corresponding file. - absolute_filepath: Path - - # Derived from full file path, contains part of the path which is relative - # to a desired base path. - relative_filepath: Path - - def __init__(self, base_dir: Path, filepath: Path): - self.base_dir = base_dir - self.absolute_filepath = filepath.resolve() - self.relative_filepath = self.absolute_filepath.relative_to( - self.base_dir) - - @classmethod - def from_path(cls, path: Path) -> 'AbsoluteAndRelativeFileName': - assert path.is_absolute() - assert path.is_file() - - base_dir = path.parent - return AbsoluteAndRelativeFileName(base_dir, path) - - @classmethod - def recursively_from_directory(cls, base_dir: Path) \ - -> List['AbsoluteAndRelativeFileName']: - """ - Create list of AbsoluteAndRelativeFileName for all the files in the - given directory. - - NOTE: Result will be pointing to a resolved paths. - """ - assert base_dir.is_absolute() - assert base_dir.is_dir() - - base_dir = base_dir.resolve() - - result = [] - for filename in base_dir.glob('**/*'): - if not filename.is_file(): - continue - result.append(AbsoluteAndRelativeFileName(base_dir, filename)) - return result diff --git a/build_files/buildbot/codesign/archive_with_indicator.py b/build_files/buildbot/codesign/archive_with_indicator.py deleted file mode 100644 index aebf5a15417..00000000000 --- a/build_files/buildbot/codesign/archive_with_indicator.py +++ /dev/null @@ -1,245 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import dataclasses -import json -import os - -from pathlib import Path -from typing import Optional - -import codesign.util as util - - -class ArchiveStateError(Exception): - message: str - - def __init__(self, message): - self.message = message - super().__init__(self.message) - - -@dataclasses.dataclass -class ArchiveState: - """ - Additional information (state) of the archive - - Includes information like expected file size of the archive file in the case - the archive file is expected to be successfully created. - - If the archive can not be created, this state will contain error message - indicating details of error. - """ - - # Size in bytes of the corresponding archive. - file_size: Optional[int] = None - - # Non-empty value indicates that error has happenned. - error_message: str = '' - - def has_error(self) -> bool: - """ - Check whether the archive is at error state - """ - - return self.error_message - - def serialize_to_string(self) -> str: - payload = dataclasses.asdict(self) - return json.dumps(payload, sort_keys=True, indent=4) - - def serialize_to_file(self, filepath: Path) -> None: - string = self.serialize_to_string() - filepath.write_text(string) - - @classmethod - def deserialize_from_string(cls, string: str) -> 'ArchiveState': - try: - object_as_dict = json.loads(string) - except json.decoder.JSONDecodeError: - raise ArchiveStateError('Error parsing JSON') - - return cls(**object_as_dict) - - @classmethod - def deserialize_from_file(cls, filepath: Path): - string = filepath.read_text() - return cls.deserialize_from_string(string) - - -class ArchiveWithIndicator: - """ - The idea of this class is to wrap around logic which takes care of keeping - track of a name of an archive and synchronization routines between buildbot - worker and signing server. - - The synchronization is done based on creating a special file after the - archive file is knowingly ready for access. - """ - - # Base directory where the archive is stored (basically, a basename() of - # the absolute archive file name). - # - # For example, 'X:\\TEMP\\'. - base_dir: Path - - # Absolute file name of the archive. - # - # For example, 'X:\\TEMP\\FOO.ZIP'. - archive_filepath: Path - - # Absolute name of a file which acts as an indication of the fact that the - # archive is ready and is available for access. - # - # This is how synchronization between buildbot worker and signing server is - # done: - # - First, the archive is created under archive_filepath name. - # - Second, the indication file is created under ready_indicator_filepath - # name. - # - Third, the colleague of whoever created the indicator name watches for - # the indication file to appear, and once it's there it access the - # archive. - ready_indicator_filepath: Path - - def __init__( - self, base_dir: Path, archive_name: str, ready_indicator_name: str): - """ - Construct the object from given base directory and name of the archive - file: - ArchiveWithIndicator(Path('X:\\TEMP'), 'FOO.ZIP', 'INPUT_READY') - """ - - self.base_dir = base_dir - self.archive_filepath = self.base_dir / archive_name - self.ready_indicator_filepath = self.base_dir / ready_indicator_name - - def is_ready_unsafe(self) -> bool: - """ - Check whether the archive is ready for access. - - No guarding about possible network failres is done here. - """ - if not self.ready_indicator_filepath.exists(): - return False - - try: - archive_state = ArchiveState.deserialize_from_file( - self.ready_indicator_filepath) - except ArchiveStateError as error: - print(f'Error deserializing archive state: {error.message}') - return False - - if archive_state.has_error(): - # If the error did happen during codesign procedure there will be no - # corresponding archive file. - # The caller code will deal with the error check further. - return True - - # Sometimes on macOS indicator file appears prior to the actual archive - # despite the order of creation and os.sync() used in tag_ready(). - # So consider archive not ready if there is an indicator without an - # actual archive. - if not self.archive_filepath.exists(): - print('Found indicator without actual archive, waiting for archive ' - f'({self.archive_filepath}) to appear.') - return False - - # Wait for until archive is fully stored. - actual_archive_size = self.archive_filepath.stat().st_size - if actual_archive_size != archive_state.file_size: - print('Partial/invalid archive size (expected ' - f'{archive_state.file_size} got {actual_archive_size})') - return False - - return True - - def is_ready(self) -> bool: - """ - Check whether the archive is ready for access. - - Will tolerate possible network failures: if there is a network failure - or if there is still no proper permission on a file False is returned. - """ - - # There are some intermitten problem happening at a random which is - # translates to "OSError : [WinError 59] An unexpected network error occurred". - # Some reports suggests it might be due to lack of permissions to the file, - # which might be applicable in our case since it's possible that file is - # initially created with non-accessible permissions and gets chmod-ed - # after initial creation. - try: - return self.is_ready_unsafe() - except OSError as e: - print(f'Exception checking archive: {e}') - return False - - def tag_ready(self, error_message='') -> None: - """ - Tag the archive as ready by creating the corresponding indication file. - - NOTE: It is expected that the archive was never tagged as ready before - and that there are no subsequent tags of the same archive. - If it is violated, an assert will fail. - """ - assert not self.is_ready() - - # Try the best to make sure everything is synced to the file system, - # to avoid any possibility of stamp appearing on a network share prior to - # an actual file. - if util.get_current_platform() != util.Platform.WINDOWS: - os.sync() - - archive_size = -1 - if self.archive_filepath.exists(): - archive_size = self.archive_filepath.stat().st_size - - archive_info = ArchiveState( - file_size=archive_size, error_message=error_message) - - self.ready_indicator_filepath.write_text( - archive_info.serialize_to_string()) - - def get_state(self) -> ArchiveState: - """ - Get state object for this archive - - The state is read from the corresponding state file. - """ - - try: - return ArchiveState.deserialize_from_file(self.ready_indicator_filepath) - except ArchiveStateError as error: - return ArchiveState(error_message=f'Error in information format: {error}') - - def clean(self) -> None: - """ - Remove both archive and the ready indication file. - """ - util.ensure_file_does_not_exist_or_die(self.ready_indicator_filepath) - util.ensure_file_does_not_exist_or_die(self.archive_filepath) - - def is_fully_absent(self) -> bool: - """ - Check whether both archive and its ready indicator are absent. - Is used for a sanity check during code signing process by both - buildbot worker and signing server. - """ - return (not self.archive_filepath.exists() and - not self.ready_indicator_filepath.exists()) diff --git a/build_files/buildbot/codesign/base_code_signer.py b/build_files/buildbot/codesign/base_code_signer.py deleted file mode 100644 index f045e9c8242..00000000000 --- a/build_files/buildbot/codesign/base_code_signer.py +++ /dev/null @@ -1,501 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Signing process overview. -# -# From buildbot worker side: -# - Files which needs to be signed are collected from either a directory to -# sign all signable files in there, or by filename of a single file to sign. -# - Those files gets packed into an archive and stored in a location location -# which is watched by the signing server. -# - A marker READY file is created which indicates the archive is ready for -# access. -# - Wait for the server to provide an archive with signed files. -# This is done by watching for the READY file which corresponds to an archive -# coming from the signing server. -# - Unpack the signed signed files from the archives and replace original ones. -# -# From code sign server: -# - Watch special location for a READY file which indicates the there is an -# archive with files which are to be signed. -# - Unpack the archive to a temporary location. -# - Run codesign tool and make sure all the files are signed. -# - Pack the signed files and store them in a location which is watched by -# the buildbot worker. -# - Create a READY file which indicates that the archive with signed files is -# ready. - -import abc -import logging -import shutil -import subprocess -import time -import tarfile -import uuid - -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import Iterable, List - -import codesign.util as util - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.archive_with_indicator import ArchiveWithIndicator -from codesign.exception import CodeSignException - - -logger = logging.getLogger(__name__) -logger_builder = logger.getChild('builder') -logger_server = logger.getChild('server') - - -def pack_files(files: Iterable[AbsoluteAndRelativeFileName], - archive_filepath: Path) -> None: - """ - Create tar archive from given files for the signing pipeline. - Is used by buildbot worker to create an archive of files which are to be - signed, and by signing server to send signed files back to the worker. - """ - with tarfile.TarFile.open(archive_filepath, 'w') as tar_file_handle: - for file_info in files: - tar_file_handle.add(file_info.absolute_filepath, - arcname=file_info.relative_filepath) - - -def extract_files(archive_filepath: Path, - extraction_dir: Path) -> None: - """ - Extract all files form the given archive into the given direcotry. - """ - - # TODO(sergey): Verify files in the archive have relative path. - - with tarfile.TarFile.open(archive_filepath, mode='r') as tar_file_handle: - tar_file_handle.extractall(path=extraction_dir) - - -class BaseCodeSigner(metaclass=abc.ABCMeta): - """ - Base class for a platform-specific signer of binaries. - - Contains all the logic shared across platform-specific implementations, such - as synchronization and notification logic. - - Platform specific bits (such as actual command for signing the binary) are - to be implemented as a subclass. - - Provides utilities code signing as a whole, including functionality needed - by a signing server and a buildbot worker. - - The signer and builder may run on separate machines, the only requirement is - that they have access to a directory which is shared between them. For the - security concerns this is to be done as a separate machine (or as a Shared - Folder configuration in VirtualBox configuration). This directory might be - mounted under different base paths, but its underlying storage is to be - the same. - - The code signer is short-lived on a buildbot worker side, and is living - forever on a code signing server side. - """ - - # TODO(sergey): Find a neat way to have config annotated. - # config: Config - - # Storage directory where builder puts files which are requested to be - # signed. - # Consider this an input of the code signing server. - unsigned_storage_dir: Path - - # Storage where signed files are stored. - # Consider this an output of the code signer server. - signed_storage_dir: Path - - # Platform the code is currently executing on. - platform: util.Platform - - def __init__(self, config): - self.config = config - - absolute_shared_storage_dir = config.SHARED_STORAGE_DIR.resolve() - - # Unsigned (signing server input) configuration. - self.unsigned_storage_dir = absolute_shared_storage_dir / 'unsigned' - - # Signed (signing server output) configuration. - self.signed_storage_dir = absolute_shared_storage_dir / 'signed' - - self.platform = util.get_current_platform() - - def cleanup_environment_for_builder(self) -> None: - # TODO(sergey): Revisit need of cleaning up the existing files. - # In practice it wasn't so helpful, and with multiple clients - # talking to the same server it becomes even more tricky. - pass - - def cleanup_environment_for_signing_server(self) -> None: - # TODO(sergey): Revisit need of cleaning up the existing files. - # In practice it wasn't so helpful, and with multiple clients - # talking to the same server it becomes even more tricky. - pass - - def generate_request_id(self) -> str: - """ - Generate an unique identifier for code signing request. - """ - return str(uuid.uuid4()) - - def archive_info_for_request_id( - self, path: Path, request_id: str) -> ArchiveWithIndicator: - return ArchiveWithIndicator( - path, f'{request_id}.tar', f'{request_id}.ready') - - def signed_archive_info_for_request_id( - self, request_id: str) -> ArchiveWithIndicator: - return self.archive_info_for_request_id( - self.signed_storage_dir, request_id) - - def unsigned_archive_info_for_request_id( - self, request_id: str) -> ArchiveWithIndicator: - return self.archive_info_for_request_id( - self.unsigned_storage_dir, request_id) - - ############################################################################ - # Buildbot worker side helpers. - - @abc.abstractmethod - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - """ - Check whether file is to be signed. - - Is used by both single file signing pipeline and recursive directory - signing pipeline. - - This is where code signer is to check whether file is to be signed or - not. This check might be based on a simple extension test or on actual - test whether file have a digital signature already or not. - """ - - def collect_files_to_sign(self, path: Path) \ - -> List[AbsoluteAndRelativeFileName]: - """ - Get all files which need to be signed from the given path. - - NOTE: The path might either be a file or directory. - - This function is run from the buildbot worker side. - """ - - # If there is a single file provided trust the buildbot worker that it - # is eligible for signing. - if path.is_file(): - file = AbsoluteAndRelativeFileName.from_path(path) - if not self.check_file_is_to_be_signed(file): - return [] - return [file] - - all_files = AbsoluteAndRelativeFileName.recursively_from_directory( - path) - files_to_be_signed = [file for file in all_files - if self.check_file_is_to_be_signed(file)] - return files_to_be_signed - - def wait_for_signed_archive_or_die(self, request_id) -> None: - """ - Wait until archive with signed files is available. - - Will only return if the archive with signed files is available. If there - was an error during code sign procedure the SystemExit exception is - raised, with the message set to the error reported by the codesign - server. - - Will only wait for the configured time. If that time exceeds and there - is still no responce from the signing server the application will exit - with a non-zero exit code. - - """ - - signed_archive_info = self.signed_archive_info_for_request_id( - request_id) - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - - timeout_in_seconds = self.config.TIMEOUT_IN_SECONDS - time_start = time.monotonic() - while not signed_archive_info.is_ready(): - time.sleep(1) - time_slept_in_seconds = time.monotonic() - time_start - if time_slept_in_seconds > timeout_in_seconds: - signed_archive_info.clean() - unsigned_archive_info.clean() - raise SystemExit("Signing server didn't finish signing in " - f'{timeout_in_seconds} seconds, dying :(') - - archive_state = signed_archive_info.get_state() - if archive_state.has_error(): - signed_archive_info.clean() - unsigned_archive_info.clean() - raise SystemExit( - f'Error happenned during codesign procedure: {archive_state.error_message}') - - def copy_signed_files_to_directory( - self, signed_dir: Path, destination_dir: Path) -> None: - """ - Copy all files from signed_dir to destination_dir. - - This function will overwrite any existing file. Permissions are copied - from the source files, but other metadata, such as timestamps, are not. - """ - for signed_filepath in signed_dir.glob('**/*'): - if not signed_filepath.is_file(): - continue - - relative_filepath = signed_filepath.relative_to(signed_dir) - destination_filepath = destination_dir / relative_filepath - destination_filepath.parent.mkdir(parents=True, exist_ok=True) - - shutil.copy(signed_filepath, destination_filepath) - - def run_buildbot_path_sign_pipeline(self, path: Path) -> None: - """ - Run all steps needed to make given path signed. - - Path points to an unsigned file or a directory which contains unsigned - files. - - If the path points to a single file then this file will be signed. - This is used to sign a final bundle such as .msi on Windows or .dmg on - macOS. - - NOTE: The code signed implementation might actually reject signing the - file, in which case the file will be left unsigned. This isn't anything - to be considered a failure situation, just might happen when buildbot - worker can not detect whether signing is really required in a specific - case or not. - - If the path points to a directory then code signer will sign all - signable files from it (finding them recursively). - """ - - self.cleanup_environment_for_builder() - - # Make sure storage directory exists. - self.unsigned_storage_dir.mkdir(parents=True, exist_ok=True) - - # Collect all files which needs to be signed and pack them into a single - # archive which will be sent to the signing server. - logger_builder.info('Collecting files which are to be signed...') - files = self.collect_files_to_sign(path) - if not files: - logger_builder.info('No files to be signed, ignoring.') - return - logger_builder.info('Found %d files to sign.', len(files)) - - request_id = self.generate_request_id() - signed_archive_info = self.signed_archive_info_for_request_id( - request_id) - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - - pack_files(files=files, - archive_filepath=unsigned_archive_info.archive_filepath) - unsigned_archive_info.tag_ready() - - # Wait for the signing server to finish signing. - logger_builder.info('Waiting signing server to sign the files...') - self.wait_for_signed_archive_or_die(request_id) - - # Extract signed files from archive and move files to final location. - with TemporaryDirectory(prefix='blender-buildbot-') as temp_dir_str: - unpacked_signed_files_dir = Path(temp_dir_str) - - logger_builder.info('Extracting signed files from archive...') - extract_files( - archive_filepath=signed_archive_info.archive_filepath, - extraction_dir=unpacked_signed_files_dir) - - destination_dir = path - if destination_dir.is_file(): - destination_dir = destination_dir.parent - self.copy_signed_files_to_directory( - unpacked_signed_files_dir, destination_dir) - - logger_builder.info('Removing archive with signed files...') - signed_archive_info.clean() - - ############################################################################ - # Signing server side helpers. - - def wait_for_sign_request(self) -> str: - """ - Wait for the buildbot to request signing of an archive. - - Returns an identifier of signing request. - """ - - # TOOD(sergey): Support graceful shutdown on Ctrl-C. - - logger_server.info( - f'Waiting for a request directory {self.unsigned_storage_dir} to appear.') - while not self.unsigned_storage_dir.exists(): - time.sleep(1) - - logger_server.info( - 'Waiting for a READY indicator of any signing request.') - request_id = None - while request_id is None: - for file in self.unsigned_storage_dir.iterdir(): - if file.suffix != '.ready': - continue - request_id = file.stem - logger_server.info(f'Found READY for request ID {request_id}.') - if request_id is None: - time.sleep(1) - - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - while not unsigned_archive_info.is_ready(): - time.sleep(1) - - return request_id - - @abc.abstractmethod - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - """ - Sign all files in the given directory. - - NOTE: Signing should happen in-place. - """ - - def run_signing_pipeline(self, request_id: str): - """ - Run the full signing pipeline starting from the point when buildbot - worker have requested signing. - """ - - # Make sure storage directory exists. - self.signed_storage_dir.mkdir(parents=True, exist_ok=True) - - with TemporaryDirectory(prefix='blender-codesign-') as temp_dir_str: - temp_dir = Path(temp_dir_str) - - signed_archive_info = self.signed_archive_info_for_request_id( - request_id) - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - - logger_server.info('Extracting unsigned files from archive...') - extract_files( - archive_filepath=unsigned_archive_info.archive_filepath, - extraction_dir=temp_dir) - - logger_server.info('Collecting all files which needs signing...') - files = AbsoluteAndRelativeFileName.recursively_from_directory( - temp_dir) - - logger_server.info('Signing all requested files...') - try: - self.sign_all_files(files) - except CodeSignException as error: - signed_archive_info.tag_ready(error_message=error.message) - unsigned_archive_info.clean() - logger_server.info('Signing is complete with errors.') - return - - logger_server.info('Packing signed files...') - pack_files(files=files, - archive_filepath=signed_archive_info.archive_filepath) - signed_archive_info.tag_ready() - - logger_server.info('Removing signing request...') - unsigned_archive_info.clean() - - logger_server.info('Signing is complete.') - - def run_signing_server(self): - logger_server.info('Starting new code signing server...') - self.cleanup_environment_for_signing_server() - logger_server.info('Code signing server is ready') - while True: - logger_server.info('Waiting for the signing request in %s...', - self.unsigned_storage_dir) - request_id = self.wait_for_sign_request() - - logger_server.info( - f'Beging signign procedure for request ID {request_id}.') - self.run_signing_pipeline(request_id) - - ############################################################################ - # Command executing. - # - # Abstracted to a degree that allows to run commands from a foreign - # platform. - # The goal with this is to allow performing dry-run tests of code signer - # server from other platforms (for example, to test that macOS code signer - # does what it is supposed to after doing a refactor on Linux). - - # TODO(sergey): What is the type annotation for the command? - def run_command_or_mock(self, command, platform: util.Platform) -> None: - """ - Run given command if current platform matches given one - - If the platform is different then it will only be printed allowing - to verify logic of the code signing process. - """ - - if platform != self.platform: - logger_server.info( - f'Will run command for {platform}: {command}') - return - - logger_server.info(f'Running command: {command}') - subprocess.run(command) - - # TODO(sergey): What is the type annotation for the command? - def check_output_or_mock(self, command, - platform: util.Platform, - allow_nonzero_exit_code=False) -> str: - """ - Run given command if current platform matches given one - - If the platform is different then it will only be printed allowing - to verify logic of the code signing process. - - If allow_nonzero_exit_code is truth then the output will be returned - even if application quit with non-zero exit code. - Otherwise an subprocess.CalledProcessError exception will be raised - in such case. - """ - - if platform != self.platform: - logger_server.info( - f'Will run command for {platform}: {command}') - return - - if allow_nonzero_exit_code: - process = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output = process.communicate()[0] - return output.decode() - - logger_server.info(f'Running command: {command}') - return subprocess.check_output( - command, stderr=subprocess.STDOUT).decode() diff --git a/build_files/buildbot/codesign/config_builder.py b/build_files/buildbot/codesign/config_builder.py deleted file mode 100644 index 1f41619ba13..00000000000 --- a/build_files/buildbot/codesign/config_builder.py +++ /dev/null @@ -1,62 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Configuration of a code signer which is specific to the code running from -# buildbot's worker. - -import sys - -from pathlib import Path - -import codesign.util as util - -from codesign.config_common import * - -platform = util.get_current_platform() -if platform == util.Platform.LINUX: - SHARED_STORAGE_DIR = Path('/data/codesign') -elif platform == util.Platform.WINDOWS: - SHARED_STORAGE_DIR = Path('Z:\\codesign') -elif platform == util.Platform.MACOS: - SHARED_STORAGE_DIR = Path('/Volumes/codesign_macos/codesign') - -# https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema -LOGGING = { - 'version': 1, - 'formatters': { - 'default': {'format': '%(asctime)-15s %(levelname)8s %(name)s %(message)s'} - }, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - 'formatter': 'default', - 'stream': 'ext://sys.stderr', - } - }, - 'loggers': { - 'codesign': {'level': 'INFO'}, - }, - 'root': { - 'level': 'WARNING', - 'handlers': [ - 'console', - ], - } -} diff --git a/build_files/buildbot/codesign/config_common.py b/build_files/buildbot/codesign/config_common.py deleted file mode 100644 index a37bc731dc0..00000000000 --- a/build_files/buildbot/codesign/config_common.py +++ /dev/null @@ -1,36 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -from pathlib import Path - -# Timeout in seconds for the signing process. -# -# This is how long buildbot packing step will wait signing server to -# perform signing. -# -# NOTE: Notarization could take a long time, hence the rather high value -# here. Might consider using different timeout for different platforms. -TIMEOUT_IN_SECONDS = 45 * 60 * 60 - -# Directory which is shared across buildbot worker and signing server. -# -# This is where worker puts files requested for signing as well as where -# server puts signed files. -SHARED_STORAGE_DIR: Path diff --git a/build_files/buildbot/codesign/config_server_template.py b/build_files/buildbot/codesign/config_server_template.py deleted file mode 100644 index ff97ed15fa5..00000000000 --- a/build_files/buildbot/codesign/config_server_template.py +++ /dev/null @@ -1,101 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Configuration of a code signer which is specific to the code signing server. -# -# NOTE: DO NOT put any sensitive information here, put it in an actual -# configuration on the signing machine. - -from pathlib import Path - -from codesign.config_common import * - -CODESIGN_DIRECTORY = Path(__file__).absolute().parent -BLENDER_GIT_ROOT_DIRECTORY = CODESIGN_DIRECTORY.parent.parent.parent - -################################################################################ -# Common configuration. - -# Directory where folders for codesign requests and signed result are stored. -# For example, /data/codesign -SHARED_STORAGE_DIR: Path - -################################################################################ -# macOS-specific configuration. - -MACOS_ENTITLEMENTS_FILE = \ - BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' / 'entitlements.plist' - -# Identity of the Developer ID Application certificate which is to be used for -# codesign tool. -# Use `security find-identity -v -p codesigning` to find the identity. -# -# NOTE: This identity is just an example from release/darwin/README.txt. -MACOS_CODESIGN_IDENTITY = 'AE825E26F12D08B692F360133210AF46F4CF7B97' - -# User name (Apple ID) which will be used to request notarization. -MACOS_XCRUN_USERNAME = 'me@example.com' - -# One-time application password which will be used to request notarization. -MACOS_XCRUN_PASSWORD = '@keychain:altool-password' - -# Timeout in seconds within which the notarial office is supposed to reply. -MACOS_NOTARIZE_TIMEOUT_IN_SECONDS = 60 * 60 - -################################################################################ -# Windows-specific configuration. - -# URL to the timestamping authority. -WIN_TIMESTAMP_AUTHORITY_URL = 'http://timestamp.digicert.com' - -# Full path to the certificate used for signing. -# -# The path and expected file format might vary depending on a platform. -# -# On Windows it is usually is a PKCS #12 key (.pfx), so the path will look -# like Path('C:\\Secret\\Blender.pfx'). -WIN_CERTIFICATE_FILEPATH: Path - -################################################################################ -# Logging configuration, common for all platforms. - -# https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema -LOGGING = { - 'version': 1, - 'formatters': { - 'default': {'format': '%(asctime)-15s %(levelname)8s %(name)s %(message)s'} - }, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - 'formatter': 'default', - 'stream': 'ext://sys.stderr', - } - }, - 'loggers': { - 'codesign': {'level': 'INFO'}, - }, - 'root': { - 'level': 'WARNING', - 'handlers': [ - 'console', - ], - } -} diff --git a/build_files/buildbot/codesign/exception.py b/build_files/buildbot/codesign/exception.py deleted file mode 100644 index 6c8a9f262a5..00000000000 --- a/build_files/buildbot/codesign/exception.py +++ /dev/null @@ -1,26 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -class CodeSignException(Exception): - message: str - - def __init__(self, message): - self.message = message - super().__init__(self.message) diff --git a/build_files/buildbot/codesign/linux_code_signer.py b/build_files/buildbot/codesign/linux_code_signer.py deleted file mode 100644 index 04935f67832..00000000000 --- a/build_files/buildbot/codesign/linux_code_signer.py +++ /dev/null @@ -1,72 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# NOTE: This is a no-op signer (since there isn't really a procedure to sign -# Linux binaries yet). Used to debug and verify the code signing routines on -# a Linux environment. - -import logging - -from pathlib import Path -from typing import List - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.base_code_signer import BaseCodeSigner - -logger = logging.getLogger(__name__) -logger_server = logger.getChild('server') - - -class LinuxCodeSigner(BaseCodeSigner): - def is_active(self) -> bool: - """ - Check whether this signer is active. - - if it is inactive, no files will be signed. - - Is used to be able to debug code signing pipeline on Linux, where there - is no code signing happening in the actual buildbot and release - environment. - """ - return False - - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - if file.relative_filepath == Path('blender'): - return True - if (file.relative_filepath.parts[-3:-1] == ('python', 'bin') and - file.relative_filepath.name.startwith('python')): - return True - if file.relative_filepath.suffix == '.so': - return True - return False - - def collect_files_to_sign(self, path: Path) \ - -> List[AbsoluteAndRelativeFileName]: - if not self.is_active(): - return [] - - return super().collect_files_to_sign(path) - - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - num_files = len(files) - for file_index, file in enumerate(files): - logger.info('Server: Signed file [%d/%d] %s', - file_index + 1, num_files, file.relative_filepath) diff --git a/build_files/buildbot/codesign/macos_code_signer.py b/build_files/buildbot/codesign/macos_code_signer.py deleted file mode 100644 index f03dad8e1d6..00000000000 --- a/build_files/buildbot/codesign/macos_code_signer.py +++ /dev/null @@ -1,456 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import logging -import re -import stat -import subprocess -import time - -from pathlib import Path -from typing import List - -import codesign.util as util - -from buildbot_utils import Builder - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.base_code_signer import BaseCodeSigner -from codesign.exception import CodeSignException - -logger = logging.getLogger(__name__) -logger_server = logger.getChild('server') - -# NOTE: Check is done as filename.endswith(), so keep the dot -EXTENSIONS_TO_BE_SIGNED = {'.dylib', '.so', '.dmg'} - -# Prefixes of a file (not directory) name which are to be signed. -# Used to sign extra executable files in Contents/Resources. -NAME_PREFIXES_TO_BE_SIGNED = {'python'} - - -class NotarizationException(CodeSignException): - pass - - -def is_file_from_bundle(file: AbsoluteAndRelativeFileName) -> bool: - """ - Check whether file is coming from an .app bundle - """ - parts = file.relative_filepath.parts - if not parts: - return False - if not parts[0].endswith('.app'): - return False - return True - - -def get_bundle_from_file( - file: AbsoluteAndRelativeFileName) -> AbsoluteAndRelativeFileName: - """ - Get AbsoluteAndRelativeFileName descriptor of bundle - """ - assert(is_file_from_bundle(file)) - - parts = file.relative_filepath.parts - bundle_name = parts[0] - - base_dir = file.base_dir - bundle_filepath = file.base_dir / bundle_name - return AbsoluteAndRelativeFileName(base_dir, bundle_filepath) - - -def is_bundle_executable_file(file: AbsoluteAndRelativeFileName) -> bool: - """ - Check whether given file is an executable within an app bundle - """ - if not is_file_from_bundle(file): - return False - - parts = file.relative_filepath.parts - num_parts = len(parts) - if num_parts < 3: - return False - - if parts[1:3] != ('Contents', 'MacOS'): - return False - - return True - - -def xcrun_field_value_from_output(field: str, output: str) -> str: - """ - Get value of a given field from xcrun output. - - If field is not found empty string is returned. - """ - - field_prefix = field + ': ' - for line in output.splitlines(): - line = line.strip() - if line.startswith(field_prefix): - return line[len(field_prefix):] - return '' - - -class MacOSCodeSigner(BaseCodeSigner): - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - if file.relative_filepath.name.startswith('.'): - return False - - if is_bundle_executable_file(file): - return True - - base_name = file.relative_filepath.name - if any(base_name.startswith(prefix) - for prefix in NAME_PREFIXES_TO_BE_SIGNED): - return True - - mode = file.absolute_filepath.lstat().st_mode - if mode & stat.S_IXUSR != 0: - file_output = subprocess.check_output( - ("file", file.absolute_filepath)).decode() - if "64-bit executable" in file_output: - return True - - return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED - - def collect_files_to_sign(self, path: Path) \ - -> List[AbsoluteAndRelativeFileName]: - # Include all files when signing app or dmg bundle: all the files are - # needed to do valid signature of bundle. - if path.name.endswith('.app'): - return AbsoluteAndRelativeFileName.recursively_from_directory(path) - if path.is_dir(): - files = [] - for child in path.iterdir(): - if child.name.endswith('.app'): - current_files = AbsoluteAndRelativeFileName.recursively_from_directory( - child) - else: - current_files = super().collect_files_to_sign(child) - for current_file in current_files: - files.append(AbsoluteAndRelativeFileName( - path, current_file.absolute_filepath)) - return files - return super().collect_files_to_sign(path) - - ############################################################################ - # Codesign. - - def codesign_remove_signature( - self, file: AbsoluteAndRelativeFileName) -> None: - """ - Make sure given file does not have codesign signature - - This is needed because codesigning is not possible for file which has - signature already. - """ - - logger_server.info( - 'Removing codesign signature from %s...', file.relative_filepath) - - command = ['codesign', '--remove-signature', file.absolute_filepath] - self.run_command_or_mock(command, util.Platform.MACOS) - - def codesign_file( - self, file: AbsoluteAndRelativeFileName) -> None: - """ - Sign given file - - NOTE: File must not have any signatures. - """ - - logger_server.info( - 'Codesigning %s...', file.relative_filepath) - - entitlements_file = self.config.MACOS_ENTITLEMENTS_FILE - command = ['codesign', - '--timestamp', - '--options', 'runtime', - f'--entitlements={entitlements_file}', - '--sign', self.config.MACOS_CODESIGN_IDENTITY, - file.absolute_filepath] - self.run_command_or_mock(command, util.Platform.MACOS) - - def codesign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - """ - Run codesign tool on all eligible files in the given list. - - Will ignore all files which are not to be signed. For the rest will - remove possible existing signature and add a new signature. - """ - - num_files = len(files) - have_ignored_files = False - signed_files = [] - for file_index, file in enumerate(files): - # Ignore file if it is not to be signed. - # Allows to manually construct ZIP of a bundle and get it signed. - if not self.check_file_is_to_be_signed(file): - logger_server.info( - 'Ignoring file [%d/%d] %s', - file_index + 1, num_files, file.relative_filepath) - have_ignored_files = True - continue - - logger_server.info( - 'Running codesigning routines for file [%d/%d] %s...', - file_index + 1, num_files, file.relative_filepath) - - self.codesign_remove_signature(file) - self.codesign_file(file) - - signed_files.append(file) - - if have_ignored_files: - logger_server.info('Signed %d files:', len(signed_files)) - num_signed_files = len(signed_files) - for file_index, signed_file in enumerate(signed_files): - logger_server.info( - '- [%d/%d] %s', - file_index + 1, num_signed_files, - signed_file.relative_filepath) - - def codesign_bundles( - self, files: List[AbsoluteAndRelativeFileName]) -> None: - """ - Codesign all .app bundles in the given list of files. - - Bundle is deducted from paths of the files, and every bundle is only - signed once. - """ - - signed_bundles = set() - extra_files = [] - - for file in files: - if not is_file_from_bundle(file): - continue - bundle = get_bundle_from_file(file) - bundle_name = bundle.relative_filepath - if bundle_name in signed_bundles: - continue - - logger_server.info('Running codesign routines on bundle %s', - bundle_name) - - # It is not possible to remove signature from DMG. - if bundle.relative_filepath.name.endswith('.app'): - self.codesign_remove_signature(bundle) - self.codesign_file(bundle) - - signed_bundles.add(bundle_name) - - # Codesign on a bundle adds an extra folder with information. - # It needs to be compied to the source. - code_signature_directory = \ - bundle.absolute_filepath / 'Contents' / '_CodeSignature' - code_signature_files = \ - AbsoluteAndRelativeFileName.recursively_from_directory( - code_signature_directory) - for code_signature_file in code_signature_files: - bundle_relative_file = AbsoluteAndRelativeFileName( - bundle.base_dir, - code_signature_directory / - code_signature_file.relative_filepath) - extra_files.append(bundle_relative_file) - - files.extend(extra_files) - - ############################################################################ - # Notarization. - - def notarize_get_bundle_id(self, file: AbsoluteAndRelativeFileName) -> str: - """ - Get bundle ID which will be used to notarize DMG - """ - name = file.relative_filepath.name - app_name = name.split('-', 2)[0].lower() - - app_name_words = app_name.split() - if len(app_name_words) > 1: - app_name_id = ''.join(word.capitalize() for word in app_name_words) - else: - app_name_id = app_name_words[0] - - # TODO(sergey): Consider using "alpha" for buildbot builds. - return f'org.blenderfoundation.{app_name_id}.release' - - def notarize_request(self, file) -> str: - """ - Request notarization of the given file. - - Returns UUID of the notarization request. If error occurred None is - returned instead of UUID. - """ - - bundle_id = self.notarize_get_bundle_id(file) - logger_server.info('Bundle ID: %s', bundle_id) - - logger_server.info('Submitting file to the notarial office.') - command = [ - 'xcrun', 'altool', '--notarize-app', '--verbose', - '-f', file.absolute_filepath, - '--primary-bundle-id', bundle_id, - '--username', self.config.MACOS_XCRUN_USERNAME, - '--password', self.config.MACOS_XCRUN_PASSWORD] - - output = self.check_output_or_mock( - command, util.Platform.MACOS, allow_nonzero_exit_code=True) - - for line in output.splitlines(): - line = line.strip() - if line.startswith('RequestUUID = '): - request_uuid = line[14:] - return request_uuid - - # Check whether the package has been already submitted. - if 'The software asset has already been uploaded.' in line: - request_uuid = re.sub( - '.*The upload ID is ([A-Fa-f0-9\-]+).*', '\\1', line) - logger_server.warning( - f'The package has been already submitted under UUID {request_uuid}') - return request_uuid - - logger_server.error(output) - logger_server.error('xcrun command did not report RequestUUID') - return None - - def notarize_review_status(self, xcrun_output: str) -> bool: - """ - Review status returned by xcrun's notarization info - - Returns truth if the notarization process has finished. - If there are errors during notarization, a NotarizationException() - exception is thrown with status message from the notarial office. - """ - - # Parse status and message - status = xcrun_field_value_from_output('Status', xcrun_output) - status_message = xcrun_field_value_from_output( - 'Status Message', xcrun_output) - - if status == 'success': - logger_server.info( - 'Package successfully notarized: %s', status_message) - return True - - if status == 'invalid': - logger_server.error(xcrun_output) - logger_server.error( - 'Package notarization has failed: %s', status_message) - raise NotarizationException(status_message) - - if status == 'in progress': - return False - - logger_server.info( - 'Unknown notarization status %s (%s)', status, status_message) - - return False - - def notarize_wait_result(self, request_uuid: str) -> None: - """ - Wait for until notarial office have a reply - """ - - logger_server.info( - 'Waiting for a result from the notarization office.') - - command = ['xcrun', 'altool', - '--notarization-info', request_uuid, - '--username', self.config.MACOS_XCRUN_USERNAME, - '--password', self.config.MACOS_XCRUN_PASSWORD] - - time_start = time.monotonic() - timeout_in_seconds = self.config.MACOS_NOTARIZE_TIMEOUT_IN_SECONDS - - while True: - xcrun_output = self.check_output_or_mock( - command, util.Platform.MACOS, allow_nonzero_exit_code=True) - - if self.notarize_review_status(xcrun_output): - break - - logger_server.info('Keep waiting for notarization office.') - time.sleep(30) - - time_slept_in_seconds = time.monotonic() - time_start - if time_slept_in_seconds > timeout_in_seconds: - logger_server.error( - "Notarial office didn't reply in %f seconds.", - timeout_in_seconds) - - def notarize_staple(self, file: AbsoluteAndRelativeFileName) -> bool: - """ - Staple notarial label on the file - """ - - logger_server.info('Stapling notarial stamp.') - - command = ['xcrun', 'stapler', 'staple', '-v', file.absolute_filepath] - self.check_output_or_mock(command, util.Platform.MACOS) - - def notarize_dmg(self, file: AbsoluteAndRelativeFileName) -> bool: - """ - Run entire pipeline to get DMG notarized. - """ - logger_server.info('Begin notarization routines on %s', - file.relative_filepath) - - # Submit file for notarization. - request_uuid = self.notarize_request(file) - if not request_uuid: - return False - logger_server.info('Received Request UUID: %s', request_uuid) - - # Wait for the status from the notarization office. - if not self.notarize_wait_result(request_uuid): - return False - - # Staple. - self.notarize_staple(file) - - def notarize_all_dmg( - self, files: List[AbsoluteAndRelativeFileName]) -> bool: - """ - Notarize all DMG images from the input. - - Images are supposed to be codesigned already. - """ - for file in files: - if not file.relative_filepath.name.endswith('.dmg'): - continue - if not self.check_file_is_to_be_signed(file): - continue - - self.notarize_dmg(file) - - ############################################################################ - # Entry point. - - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - # TODO(sergey): Handle errors somehow. - - self.codesign_all_files(files) - self.codesign_bundles(files) - self.notarize_all_dmg(files) diff --git a/build_files/buildbot/codesign/simple_code_signer.py b/build_files/buildbot/codesign/simple_code_signer.py deleted file mode 100644 index 674d9e9ce9e..00000000000 --- a/build_files/buildbot/codesign/simple_code_signer.py +++ /dev/null @@ -1,52 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - - -import logging.config -import sys - -from pathlib import Path -from typing import Optional - -import codesign.config_builder -import codesign.util as util -from codesign.base_code_signer import BaseCodeSigner - - -class SimpleCodeSigner: - code_signer: Optional[BaseCodeSigner] - - def __init__(self): - platform = util.get_current_platform() - if platform == util.Platform.LINUX: - from codesign.linux_code_signer import LinuxCodeSigner - self.code_signer = LinuxCodeSigner(codesign.config_builder) - elif platform == util.Platform.MACOS: - from codesign.macos_code_signer import MacOSCodeSigner - self.code_signer = MacOSCodeSigner(codesign.config_builder) - elif platform == util.Platform.WINDOWS: - from codesign.windows_code_signer import WindowsCodeSigner - self.code_signer = WindowsCodeSigner(codesign.config_builder) - else: - self.code_signer = None - - def sign_file_or_directory(self, path: Path) -> None: - logging.config.dictConfig(codesign.config_builder.LOGGING) - self.code_signer.run_buildbot_path_sign_pipeline(path) diff --git a/build_files/buildbot/codesign/util.py b/build_files/buildbot/codesign/util.py deleted file mode 100644 index e67292dd049..00000000000 --- a/build_files/buildbot/codesign/util.py +++ /dev/null @@ -1,54 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import sys - -from enum import Enum -from pathlib import Path - - -class Platform(Enum): - LINUX = 1 - MACOS = 2 - WINDOWS = 3 - - -def get_current_platform() -> Platform: - if sys.platform == 'linux': - return Platform.LINUX - elif sys.platform == 'darwin': - return Platform.MACOS - elif sys.platform == 'win32': - return Platform.WINDOWS - raise Exception(f'Unknown platform {sys.platform}') - - -def ensure_file_does_not_exist_or_die(filepath: Path) -> None: - """ - If the file exists, unlink it. - If the file path exists and is not a file an assert will trigger. - If the file path does not exists nothing happens. - """ - if not filepath.exists(): - return - if not filepath.is_file(): - # TODO(sergey): Provide information about what the filepath actually is. - raise SystemExit(f'{filepath} is expected to be a file, but is not') - filepath.unlink() diff --git a/build_files/buildbot/codesign/windows_code_signer.py b/build_files/buildbot/codesign/windows_code_signer.py deleted file mode 100644 index db185788a56..00000000000 --- a/build_files/buildbot/codesign/windows_code_signer.py +++ /dev/null @@ -1,117 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import logging - -from pathlib import Path -from typing import List - -import codesign.util as util - -from buildbot_utils import Builder - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.base_code_signer import BaseCodeSigner -from codesign.exception import CodeSignException - -logger = logging.getLogger(__name__) -logger_server = logger.getChild('server') - -# NOTE: Check is done as filename.endswith(), so keep the dot -EXTENSIONS_TO_BE_SIGNED = {'.exe', '.dll', '.pyd', '.msi'} - -BLACKLIST_FILE_PREFIXES = ( - 'api-ms-', 'concrt', 'msvcp', 'ucrtbase', 'vcomp', 'vcruntime') - - -class SigntoolException(CodeSignException): - pass - -class WindowsCodeSigner(BaseCodeSigner): - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - base_name = file.relative_filepath.name - if any(base_name.startswith(prefix) - for prefix in BLACKLIST_FILE_PREFIXES): - return False - - return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED - - - def get_sign_command_prefix(self) -> List[str]: - return [ - 'signtool', 'sign', '/v', - '/f', self.config.WIN_CERTIFICATE_FILEPATH, - '/tr', self.config.WIN_TIMESTAMP_AUTHORITY_URL] - - - def run_codesign_tool(self, filepath: Path) -> None: - command = self.get_sign_command_prefix() + [filepath] - - try: - codesign_output = self.check_output_or_mock(command, util.Platform.WINDOWS) - except subprocess.CalledProcessError as e: - raise SigntoolException(f'Error running signtool {e}') - - logger_server.info(f'signtool output:\n{codesign_output}') - - got_number_of_success = False - - for line in codesign_output.split('\n'): - line_clean = line.strip() - line_clean_lower = line_clean.lower() - - if line_clean_lower.startswith('number of warnings') or \ - line_clean_lower.startswith('number of errors'): - number = int(line_clean_lower.split(':')[1]) - if number != 0: - raise SigntoolException('Non-clean success of signtool') - - if line_clean_lower.startswith('number of files successfully signed'): - got_number_of_success = True - number = int(line_clean_lower.split(':')[1]) - if number != 1: - raise SigntoolException('Signtool did not consider codesign a success') - - if not got_number_of_success: - raise SigntoolException('Signtool did not report number of files signed') - - - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - # NOTE: Sign files one by one to avoid possible command line length - # overflow (which could happen if we ever decide to sign every binary - # in the install folder, for example). - # - # TODO(sergey): Consider doing batched signing of handful of files in - # one go (but only if this actually known to be much faster). - num_files = len(files) - for file_index, file in enumerate(files): - # Ignore file if it is not to be signed. - # Allows to manually construct ZIP of package and get it signed. - if not self.check_file_is_to_be_signed(file): - logger_server.info( - 'Ignoring file [%d/%d] %s', - file_index + 1, num_files, file.relative_filepath) - continue - - logger_server.info( - 'Running signtool command for file [%d/%d] %s...', - file_index + 1, num_files, file.relative_filepath) - self.run_codesign_tool(file.absolute_filepath) diff --git a/build_files/buildbot/codesign_server_linux.py b/build_files/buildbot/codesign_server_linux.py deleted file mode 100755 index be3065e640d..00000000000 --- a/build_files/buildbot/codesign_server_linux.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# NOTE: This is a no-op signer (since there isn't really a procedure to sign -# Linux binaries yet). Used to debug and verify the code signing routines on -# a Linux environment. - -import logging.config -from pathlib import Path -from typing import List - -from codesign.linux_code_signer import LinuxCodeSigner -import codesign.config_server - -if __name__ == "__main__": - logging.config.dictConfig(codesign.config_server.LOGGING) - code_signer = LinuxCodeSigner(codesign.config_server) - code_signer.run_signing_server() diff --git a/build_files/buildbot/codesign_server_macos.py b/build_files/buildbot/codesign_server_macos.py deleted file mode 100755 index 1bdb012fe67..00000000000 --- a/build_files/buildbot/codesign_server_macos.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import logging.config -from pathlib import Path -from typing import List - -from codesign.macos_code_signer import MacOSCodeSigner -import codesign.config_server - -if __name__ == "__main__": - entitlements_file = codesign.config_server.MACOS_ENTITLEMENTS_FILE - if not entitlements_file.exists(): - raise SystemExit( - 'Entitlements file {entitlements_file} does not exist.') - if not entitlements_file.is_file(): - raise SystemExit( - 'Entitlements file {entitlements_file} is not a file.') - - logging.config.dictConfig(codesign.config_server.LOGGING) - code_signer = MacOSCodeSigner(codesign.config_server) - code_signer.run_signing_server() diff --git a/build_files/buildbot/codesign_server_windows.bat b/build_files/buildbot/codesign_server_windows.bat deleted file mode 100644 index 82680f30eb4..00000000000 --- a/build_files/buildbot/codesign_server_windows.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -rem This is an entry point of the codesign server for Windows. -rem It makes sure that signtool.exe is within the current PATH and can be -rem used by the Python script. - -SETLOCAL - -set PATH=C:\Program Files (x86)\Windows Kits\10\App Certification Kit;%PATH% - -codesign_server_windows.py diff --git a/build_files/buildbot/codesign_server_windows.py b/build_files/buildbot/codesign_server_windows.py deleted file mode 100755 index 97ea4fd6756..00000000000 --- a/build_files/buildbot/codesign_server_windows.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Implementation of codesign server for Windows. -# -# NOTE: If signtool.exe is not in the PATH use codesign_server_windows.bat - -import logging.config -import shutil - -from pathlib import Path -from typing import List - -import codesign.util as util - -from codesign.windows_code_signer import WindowsCodeSigner -import codesign.config_server - -if __name__ == "__main__": - logging.config.dictConfig(codesign.config_server.LOGGING) - - logger = logging.getLogger(__name__) - logger_server = logger.getChild('server') - - # TODO(sergey): Consider moving such sanity checks into - # CodeSigner.check_environment_or_die(). - if not shutil.which('signtool.exe'): - if util.get_current_platform() == util.Platform.WINDOWS: - raise SystemExit("signtool.exe is not found in %PATH%") - logger_server.info( - 'signtool.exe not found, ' - 'but will not be used on this foreign platform') - - code_signer = WindowsCodeSigner(codesign.config_server) - code_signer.run_signing_server() diff --git a/build_files/buildbot/worker_bundle_dmg.py b/build_files/buildbot/worker_bundle_dmg.py deleted file mode 100755 index 56e0d7da88e..00000000000 --- a/build_files/buildbot/worker_bundle_dmg.py +++ /dev/null @@ -1,551 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -import argparse -import re -import shutil -import subprocess -import sys -import time - -from pathlib import Path -from tempfile import TemporaryDirectory, NamedTemporaryFile -from typing import List - -BUILDBOT_DIRECTORY = Path(__file__).absolute().parent -CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'worker_codesign.py' -BLENDER_GIT_ROOT_DIRECTORY = BUILDBOT_DIRECTORY.parent.parent -DARWIN_DIRECTORY = BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' - - -# Extra size which is added on top of actual files size when estimating size -# of destination DNG. -EXTRA_DMG_SIZE_IN_BYTES = 800 * 1024 * 1024 - -################################################################################ -# Common utilities - - -def get_directory_size(root_directory: Path) -> int: - """ - Get size of directory on disk - """ - - total_size = 0 - for file in root_directory.glob('**/*'): - total_size += file.lstat().st_size - return total_size - - -################################################################################ -# DMG bundling specific logic - -def create_argument_parser(): - parser = argparse.ArgumentParser() - parser.add_argument( - 'source_dir', - type=Path, - help='Source directory which points to either existing .app bundle' - 'or to a directory with .app bundles.') - parser.add_argument( - '--background-image', - type=Path, - help="Optional background picture which will be set on the DMG." - "If not provided default Blender's one is used.") - parser.add_argument( - '--volume-name', - type=str, - help='Optional name of a volume which will be used for DMG.') - parser.add_argument( - '--dmg', - type=Path, - help='Optional argument which points to a final DMG file name.') - parser.add_argument( - '--applescript', - type=Path, - help="Optional path to applescript to set up folder looks of DMG." - "If not provided default Blender's one is used.") - parser.add_argument( - '--codesign', - action="store_true", - help="Code sign and notarize DMG contents.") - return parser - - -def collect_app_bundles(source_dir: Path) -> List[Path]: - """ - Collect all app bundles which are to be put into DMG - - If the source directory points to FOO.app it will be the only app bundle - packed. - - Otherwise all .app bundles from given directory are placed to a single - DMG. - """ - - if source_dir.name.endswith('.app'): - return [source_dir] - - app_bundles = [] - for filename in source_dir.glob('*'): - if not filename.is_dir(): - continue - if not filename.name.endswith('.app'): - continue - - app_bundles.append(filename) - - return app_bundles - - -def collect_and_log_app_bundles(source_dir: Path) -> List[Path]: - app_bundles = collect_app_bundles(source_dir) - - if not app_bundles: - print('No app bundles found for packing') - return - - print(f'Found {len(app_bundles)} to pack:') - for app_bundle in app_bundles: - print(f'- {app_bundle}') - - return app_bundles - - -def estimate_dmg_size(app_bundles: List[Path]) -> int: - """ - Estimate size of DMG to hold requested app bundles - - The size is based on actual size of all files in all bundles plus some - space to compensate for different size-on-disk plus some space to hold - codesign signatures. - - Is better to be on a high side since the empty space is compressed, but - lack of space might cause silent failures later on. - """ - - app_bundles_size = 0 - for app_bundle in app_bundles: - app_bundles_size += get_directory_size(app_bundle) - - return app_bundles_size + EXTRA_DMG_SIZE_IN_BYTES - - -def copy_app_bundles_to_directory(app_bundles: List[Path], - directory: Path) -> None: - """ - Copy all bundles to a given directory - - This directory is what the DMG will be created from. - """ - for app_bundle in app_bundles: - print(f'Copying {app_bundle.name}...') - shutil.copytree(app_bundle, directory / app_bundle.name) - - -def get_main_app_bundle(app_bundles: List[Path]) -> Path: - """ - Get application bundle main for the installation - """ - return app_bundles[0] - - -def create_dmg_image(app_bundles: List[Path], - dmg_filepath: Path, - volume_name: str) -> None: - """ - Create DMG disk image and put app bundles in it - - No DMG configuration or codesigning is happening here. - """ - - if dmg_filepath.exists(): - print(f'Removing existing writable DMG {dmg_filepath}...') - dmg_filepath.unlink() - - print('Preparing directory with app bundles for the DMG...') - with TemporaryDirectory(prefix='blender-dmg-content-') as content_dir_str: - # Copy all bundles to a clean directory. - content_dir = Path(content_dir_str) - copy_app_bundles_to_directory(app_bundles, content_dir) - - # Estimate size of the DMG. - dmg_size = estimate_dmg_size(app_bundles) - print(f'Estimated DMG size: {dmg_size:,} bytes.') - - # Create the DMG. - print(f'Creating writable DMG {dmg_filepath}') - command = ('hdiutil', - 'create', - '-size', str(dmg_size), - '-fs', 'HFS+', - '-srcfolder', content_dir, - '-volname', volume_name, - '-format', 'UDRW', - dmg_filepath) - subprocess.run(command) - - -def get_writable_dmg_filepath(dmg_filepath: Path): - """ - Get file path for writable DMG image - """ - parent = dmg_filepath.parent - return parent / (dmg_filepath.stem + '-temp.dmg') - - -def mount_readwrite_dmg(dmg_filepath: Path) -> None: - """ - Mount writable DMG - - Mounting point would be /Volumes/<volume name> - """ - - print(f'Mounting read-write DMG ${dmg_filepath}') - command = ('hdiutil', - 'attach', '-readwrite', - '-noverify', - '-noautoopen', - dmg_filepath) - subprocess.run(command) - - -def get_mount_directory_for_volume_name(volume_name: str) -> Path: - """ - Get directory under which the volume will be mounted - """ - - return Path('/Volumes') / volume_name - - -def eject_volume(volume_name: str) -> None: - """ - Eject given volume, if mounted - """ - mount_directory = get_mount_directory_for_volume_name(volume_name) - if not mount_directory.exists(): - return - mount_directory_str = str(mount_directory) - - print(f'Ejecting volume {volume_name}') - - # Figure out which device to eject. - mount_output = subprocess.check_output(['mount']).decode() - device = '' - for line in mount_output.splitlines(): - if f'on {mount_directory_str} (' not in line: - continue - tokens = line.split(' ', 3) - if len(tokens) < 3: - continue - if tokens[1] != 'on': - continue - if device: - raise Exception( - f'Multiple devices found for mounting point {mount_directory}') - device = tokens[0] - - if not device: - raise Exception( - f'No device found for mounting point {mount_directory}') - - print(f'{mount_directory} is mounted as device {device}, ejecting...') - subprocess.run(['diskutil', 'eject', device]) - - -def copy_background_if_needed(background_image_filepath: Path, - mount_directory: Path) -> None: - """ - Copy background to the DMG - - If the background image is not specified it will not be copied. - """ - - if not background_image_filepath: - print('No background image provided.') - return - - print(f'Copying background image {background_image_filepath}') - - destination_dir = mount_directory / '.background' - destination_dir.mkdir(exist_ok=True) - - destination_filepath = destination_dir / background_image_filepath.name - shutil.copy(background_image_filepath, destination_filepath) - - -def create_applications_link(mount_directory: Path) -> None: - """ - Create link to /Applications in the given location - """ - - print('Creating link to /Applications') - - command = ('ln', '-s', '/Applications', mount_directory / ' ') - subprocess.run(command) - - -def run_applescript(applescript: Path, - volume_name: str, - app_bundles: List[Path], - background_image_filepath: Path) -> None: - """ - Run given applescript to adjust look and feel of the DMG - """ - - main_app_bundle = get_main_app_bundle(app_bundles) - - with NamedTemporaryFile( - mode='w', suffix='.applescript') as temp_applescript: - print('Adjusting applescript for volume name...') - # Adjust script to the specific volume name. - with open(applescript, mode='r') as input: - for line in input.readlines(): - stripped_line = line.strip() - if stripped_line.startswith('tell disk'): - line = re.sub('tell disk ".*"', - f'tell disk "{volume_name}"', - line) - elif stripped_line.startswith('set background picture'): - if not background_image_filepath: - continue - else: - background_image_short = \ - '.background:' + background_image_filepath.name - line = re.sub('to file ".*"', - f'to file "{background_image_short}"', - line) - line = line.replace('blender.app', main_app_bundle.name) - temp_applescript.write(line) - - temp_applescript.flush() - - print('Running applescript...') - command = ('osascript', temp_applescript.name) - subprocess.run(command) - - print('Waiting for applescript...') - - # NOTE: This is copied from bundle.sh. The exact reason for sleep is - # still remained a mystery. - time.sleep(5) - - -def codesign(subject: Path): - """ - Codesign file or directory - - NOTE: For DMG it will also notarize. - """ - - command = (CODESIGN_SCRIPT, subject) - subprocess.run(command) - - -def codesign_app_bundles_in_dmg(mount_directory: str) -> None: - """ - Code sign all binaries and bundles in the mounted directory - """ - - print(f'Codesigning all app bundles in {mount_directory}') - codesign(mount_directory) - - -def codesign_and_notarize_dmg(dmg_filepath: Path) -> None: - """ - Run codesign and notarization pipeline on the DMG - """ - - print(f'Codesigning and notarizing DMG {dmg_filepath}') - codesign(dmg_filepath) - - -def compress_dmg(writable_dmg_filepath: Path, - final_dmg_filepath: Path) -> None: - """ - Compress temporary read-write DMG - """ - command = ('hdiutil', 'convert', - writable_dmg_filepath, - '-format', 'UDZO', - '-o', final_dmg_filepath) - - if final_dmg_filepath.exists(): - print(f'Removing old compressed DMG {final_dmg_filepath}') - final_dmg_filepath.unlink() - - print('Compressing disk image...') - subprocess.run(command) - - -def create_final_dmg(app_bundles: List[Path], - dmg_filepath: Path, - background_image_filepath: Path, - volume_name: str, - applescript: Path, - codesign: bool) -> None: - """ - Create DMG with all app bundles - - Will take care configuring background, signing all binaries and app bundles - and notarizing the DMG. - """ - - print('Running all routines to create final DMG') - - writable_dmg_filepath = get_writable_dmg_filepath(dmg_filepath) - mount_directory = get_mount_directory_for_volume_name(volume_name) - - # Make sure volume is not mounted. - # If it is mounted it will prevent removing old DMG files and could make - # it so app bundles are copied to the wrong place. - eject_volume(volume_name) - - create_dmg_image(app_bundles, writable_dmg_filepath, volume_name) - - mount_readwrite_dmg(writable_dmg_filepath) - - # Run codesign first, prior to copying amything else. - # - # This allows to recurs into the content of bundles without worrying about - # possible interfereice of Application symlink. - if codesign: - codesign_app_bundles_in_dmg(mount_directory) - - copy_background_if_needed(background_image_filepath, mount_directory) - create_applications_link(mount_directory) - run_applescript(applescript, volume_name, app_bundles, - background_image_filepath) - - print('Ejecting read-write DMG image...') - eject_volume(volume_name) - - compress_dmg(writable_dmg_filepath, dmg_filepath) - writable_dmg_filepath.unlink() - - if codesign: - codesign_and_notarize_dmg(dmg_filepath) - - -def ensure_dmg_extension(filepath: Path) -> Path: - """ - Make sure given file have .dmg extension - """ - - if filepath.suffix != '.dmg': - return filepath.with_suffix(f'{filepath.suffix}.dmg') - return filepath - - -def get_dmg_filepath(requested_name: Path, app_bundles: List[Path]) -> Path: - """ - Get full file path for the final DMG image - - Will use the provided one when possible, otherwise will deduct it from - app bundles. - - If the name is deducted, the DMG is stored in the current directory. - """ - - if requested_name: - return ensure_dmg_extension(requested_name.absolute()) - - # TODO(sergey): This is not necessarily the main one. - main_bundle = app_bundles[0] - # Strip .app from the name - return Path(main_bundle.name[:-4] + '.dmg').absolute() - - -def get_background_image(requested_background_image: Path) -> Path: - """ - Get effective filepath for the background image - """ - - if requested_background_image: - return requested_background_image.absolute() - - return DARWIN_DIRECTORY / 'background.tif' - - -def get_applescript(requested_applescript: Path) -> Path: - """ - Get effective filepath for the applescript - """ - - if requested_applescript: - return requested_applescript.absolute() - - return DARWIN_DIRECTORY / 'blender.applescript' - - -def get_volume_name_from_dmg_filepath(dmg_filepath: Path) -> str: - """ - Deduct volume name from the DMG path - - Will use first part of the DMG file name prior to dash. - """ - - tokens = dmg_filepath.stem.split('-') - words = tokens[0].split() - - return ' '.join(word.capitalize() for word in words) - - -def get_volume_name(requested_volume_name: str, - dmg_filepath: Path) -> str: - """ - Get effective name for DMG volume - """ - - if requested_volume_name: - return requested_volume_name - - return get_volume_name_from_dmg_filepath(dmg_filepath) - - -def main(): - parser = create_argument_parser() - args = parser.parse_args() - - # Get normalized input parameters. - source_dir = args.source_dir.absolute() - background_image_filepath = get_background_image(args.background_image) - applescript = get_applescript(args.applescript) - codesign = args.codesign - - app_bundles = collect_and_log_app_bundles(source_dir) - if not app_bundles: - return - - dmg_filepath = get_dmg_filepath(args.dmg, app_bundles) - volume_name = get_volume_name(args.volume_name, dmg_filepath) - - print(f'Will produce DMG "{dmg_filepath.name}" (without quotes)') - - create_final_dmg(app_bundles, - dmg_filepath, - background_image_filepath, - volume_name, - applescript, - codesign) - - -if __name__ == "__main__": - main() diff --git a/build_files/buildbot/worker_codesign.cmake b/build_files/buildbot/worker_codesign.cmake deleted file mode 100644 index f37feaef407..00000000000 --- a/build_files/buildbot/worker_codesign.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# This is a script which is used as POST-INSTALL one for regular CMake's -# INSTALL target. -# It is used by buildbot workers to sign every binary which is going into -# the final buundle. - -# On Windows Python 3 there only is python.exe, no python3.exe. -# -# On other platforms it is possible to have python2 and python3, and a -# symbolic link to python to either of them. So on those platforms use -# an explicit Python version. -if(WIN32) - set(PYTHON_EXECUTABLE python) -else() - set(PYTHON_EXECUTABLE python3) -endif() - -execute_process( - COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/worker_codesign.py" - "${CMAKE_INSTALL_PREFIX}" - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} - RESULT_VARIABLE exit_code -) - -if(NOT exit_code EQUAL "0") - message(FATAL_ERROR "Non-zero exit code of codesign tool") -endif() diff --git a/build_files/buildbot/worker_codesign.py b/build_files/buildbot/worker_codesign.py deleted file mode 100755 index a82ee98b1b5..00000000000 --- a/build_files/buildbot/worker_codesign.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# Helper script which takes care of signing provided location. -# -# The location can either be a directory (in which case all eligible binaries -# will be signed) or a single file (in which case a single file will be signed). -# -# This script takes care of all the complexity of communicating between process -# which requests file to be signed and the code signing server. -# -# NOTE: Signing happens in-place. - -import argparse -import sys - -from pathlib import Path - -from codesign.simple_code_signer import SimpleCodeSigner - - -def create_argument_parser(): - parser = argparse.ArgumentParser() - parser.add_argument('path_to_sign', type=Path) - return parser - - -def main(): - parser = create_argument_parser() - args = parser.parse_args() - path_to_sign = args.path_to_sign.absolute() - - if sys.platform == 'win32': - # When WIX packed is used to generate .msi on Windows the CPack will - # install two different projects and install them to different - # installation prefix: - # - # - C:\b\build\_CPack_Packages\WIX\Blender - # - C:\b\build\_CPack_Packages\WIX\Unspecified - # - # Annoying part is: CMake's post-install script will only be run - # once, with the install prefix which corresponds to a project which - # was installed last. But we want to sign binaries from all projects. - # So in order to do so we detect that we are running for a CPack's - # project used for WIX and force parent directory (which includes both - # projects) to be signed. - # - # Here we force both projects to be signed. - if path_to_sign.name == 'Unspecified' and 'WIX' in str(path_to_sign): - path_to_sign = path_to_sign.parent - - code_signer = SimpleCodeSigner() - code_signer.sign_file_or_directory(path_to_sign) - - -if __name__ == "__main__": - main() diff --git a/build_files/buildbot/worker_compile.py b/build_files/buildbot/worker_compile.py deleted file mode 100644 index 8c6b44c5866..00000000000 --- a/build_files/buildbot/worker_compile.py +++ /dev/null @@ -1,135 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import os -import shutil - -import buildbot_utils - - -def get_cmake_options(builder): - codesign_script = os.path.join( - builder.blender_dir, 'build_files', 'buildbot', 'worker_codesign.cmake') - - config_file = "build_files/cmake/config/blender_release.cmake" - options = ['-DCMAKE_BUILD_TYPE:STRING=Release', - '-DWITH_GTESTS=ON'] - - if builder.platform == 'mac': - options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64') - options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9') - elif builder.platform == 'win': - options.extend(['-G', 'Visual Studio 16 2019', '-A', 'x64']) - if builder.codesign: - options.extend(['-DPOSTINSTALL_SCRIPT:PATH=' + codesign_script]) - elif builder.platform == 'linux': - config_file = "build_files/buildbot/config/blender_linux.cmake" - - optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK-7.1') - options.append('-DOPTIX_ROOT_DIR:PATH=' + optix_sdk_dir) - - # Workaround to build sm_30 kernels with CUDA 10, since CUDA 11 no longer supports that architecture - if builder.platform == 'win': - options.append('-DCUDA10_TOOLKIT_ROOT_DIR:PATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1') - options.append('-DCUDA10_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1/bin/nvcc.exe') - options.append('-DCUDA11_TOOLKIT_ROOT_DIR:PATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.1') - options.append('-DCUDA11_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.1/bin/nvcc.exe') - elif builder.platform == 'linux': - options.append('-DCUDA10_TOOLKIT_ROOT_DIR:PATH=/usr/local/cuda-10.1') - options.append('-DCUDA10_NVCC_EXECUTABLE:FILEPATH=/usr/local/cuda-10.1/bin/nvcc') - options.append('-DCUDA11_TOOLKIT_ROOT_DIR:PATH=/usr/local/cuda-11.1') - options.append('-DCUDA11_NVCC_EXECUTABLE:FILEPATH=/usr/local/cuda-11.1/bin/nvcc') - - options.append("-C" + os.path.join(builder.blender_dir, config_file)) - options.append("-DCMAKE_INSTALL_PREFIX=%s" % (builder.install_dir)) - - return options - - -def update_git(builder): - # Do extra git fetch because not all platform/git/buildbot combinations - # update the origin remote, causing buildinfo to detect local changes. - os.chdir(builder.blender_dir) - - print("Fetching remotes") - command = ['git', 'fetch', '--all'] - buildbot_utils.call(builder.command_prefix + command) - - -def clean_directories(builder): - # Make sure no garbage remained from the previous run - if os.path.isdir(builder.install_dir): - shutil.rmtree(builder.install_dir) - - # Make sure build directory exists and enter it - os.makedirs(builder.build_dir, exist_ok=True) - - # Remove buildinfo files to force buildbot to re-generate them. - for buildinfo in ('buildinfo.h', 'buildinfo.h.txt', ): - full_path = os.path.join(builder.build_dir, 'source', 'creator', buildinfo) - if os.path.exists(full_path): - print("Removing {}" . format(buildinfo)) - os.remove(full_path) - - -def cmake_configure(builder): - # CMake configuration - os.chdir(builder.build_dir) - - cmake_cache = os.path.join(builder.build_dir, 'CMakeCache.txt') - if os.path.exists(cmake_cache): - print("Removing CMake cache") - os.remove(cmake_cache) - - print("CMake configure:") - cmake_options = get_cmake_options(builder) - command = ['cmake', builder.blender_dir] + cmake_options - buildbot_utils.call(builder.command_prefix + command) - - -def cmake_build(builder): - # CMake build - os.chdir(builder.build_dir) - - # NOTE: CPack will build an INSTALL target, which would mean that code - # signing will happen twice when using `make install` and CPack. - # The tricky bit here is that it is not possible to know whether INSTALL - # target is used by CPack or by a buildbot itaself. Extra level on top of - # this is that on Windows it is required to build INSTALL target in order - # to have unit test binaries to run. - # So on the one hand we do an extra unneeded code sign on Windows, but on - # a positive side we don't add complexity and don't make build process more - # fragile trying to avoid this. The signing process is way faster than just - # a clean build of buildbot, especially with regression tests enabled. - if builder.platform == 'win': - command = ['cmake', '--build', '.', '--target', 'install', '--config', 'Release'] - else: - command = ['make', '-s', '-j16', 'install'] - - print("CMake build:") - buildbot_utils.call(builder.command_prefix + command) - - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - update_git(builder) - clean_directories(builder) - cmake_configure(builder) - cmake_build(builder) diff --git a/build_files/buildbot/worker_pack.py b/build_files/buildbot/worker_pack.py deleted file mode 100644 index a5727a66e09..00000000000 --- a/build_files/buildbot/worker_pack.py +++ /dev/null @@ -1,208 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Runs on buildbot worker, creating a release package using the build -# system and zipping it into buildbot_upload.zip. This is then uploaded -# to the master in the next buildbot step. - -import os -import sys - -from pathlib import Path - -import buildbot_utils - - -def get_package_name(builder, platform=None): - info = buildbot_utils.VersionInfo(builder) - - package_name = 'blender-' + info.full_version - if platform: - package_name += '-' + platform - if not (builder.branch == 'master' or builder.is_release_branch): - if info.is_development_build: - package_name = builder.branch + "-" + package_name - - return package_name - - -def sign_file_or_directory(path): - from codesign.simple_code_signer import SimpleCodeSigner - code_signer = SimpleCodeSigner() - code_signer.sign_file_or_directory(Path(path)) - - -def create_buildbot_upload_zip(builder, package_files): - import zipfile - - buildbot_upload_zip = os.path.join(builder.upload_dir, "buildbot_upload.zip") - if os.path.exists(buildbot_upload_zip): - os.remove(buildbot_upload_zip) - - try: - z = zipfile.ZipFile(buildbot_upload_zip, "w", compression=zipfile.ZIP_STORED) - for filepath, filename in package_files: - print("Packaged", filename) - z.write(filepath, arcname=filename) - z.close() - except Exception as ex: - sys.stderr.write('Create buildbot_upload.zip failed: ' + str(ex) + '\n') - sys.exit(1) - - -def create_tar_xz(src, dest, package_name): - # One extra to remove leading os.sep when cleaning root for package_root - ln = len(src) + 1 - flist = list() - - # Create list of tuples containing file and archive name - for root, dirs, files in os.walk(src): - package_root = os.path.join(package_name, root[ln:]) - flist.extend([(os.path.join(root, file), os.path.join(package_root, file)) for file in files]) - - import tarfile - - # Set UID/GID of archived files to 0, otherwise they'd be owned by whatever - # user compiled the package. If root then unpacks it to /usr/local/ you get - # a security issue. - def _fakeroot(tarinfo): - tarinfo.gid = 0 - tarinfo.gname = "root" - tarinfo.uid = 0 - tarinfo.uname = "root" - return tarinfo - - package = tarfile.open(dest, 'w:xz', preset=9) - for entry in flist: - package.add(entry[0], entry[1], recursive=False, filter=_fakeroot) - package.close() - - -def cleanup_files(dirpath, extension): - for f in os.listdir(dirpath): - filepath = os.path.join(dirpath, f) - if os.path.isfile(filepath) and f.endswith(extension): - os.remove(filepath) - - -def pack_mac(builder): - info = buildbot_utils.VersionInfo(builder) - - os.chdir(builder.build_dir) - cleanup_files(builder.build_dir, '.dmg') - - package_name = get_package_name(builder, 'macOS') - package_filename = package_name + '.dmg' - package_filepath = os.path.join(builder.build_dir, package_filename) - - release_dir = os.path.join(builder.blender_dir, 'release', 'darwin') - buildbot_dir = os.path.join(builder.blender_dir, 'build_files', 'buildbot') - bundle_script = os.path.join(buildbot_dir, 'worker_bundle_dmg.py') - - command = [bundle_script] - command += ['--dmg', package_filepath] - if info.is_development_build: - background_image = os.path.join(release_dir, 'buildbot', 'background.tif') - command += ['--background-image', background_image] - if builder.codesign: - command += ['--codesign'] - command += [builder.install_dir] - buildbot_utils.call(command) - - create_buildbot_upload_zip(builder, [(package_filepath, package_filename)]) - - -def pack_win(builder): - info = buildbot_utils.VersionInfo(builder) - - os.chdir(builder.build_dir) - cleanup_files(builder.build_dir, '.zip') - - # CPack will add the platform name - cpack_name = get_package_name(builder, None) - package_name = get_package_name(builder, 'windows' + str(builder.bits)) - - command = ['cmake', '-DCPACK_OVERRIDE_PACKAGENAME:STRING=' + cpack_name, '.'] - buildbot_utils.call(builder.command_prefix + command) - command = ['cpack', '-G', 'ZIP'] - buildbot_utils.call(builder.command_prefix + command) - - package_filename = package_name + '.zip' - package_filepath = os.path.join(builder.build_dir, package_filename) - package_files = [(package_filepath, package_filename)] - - if info.version_cycle == 'release': - # Installer only for final release builds, otherwise will get - # 'this product is already installed' messages. - command = ['cpack', '-G', 'WIX'] - buildbot_utils.call(builder.command_prefix + command) - - package_filename = package_name + '.msi' - package_filepath = os.path.join(builder.build_dir, package_filename) - if builder.codesign: - sign_file_or_directory(package_filepath) - - package_files += [(package_filepath, package_filename)] - - create_buildbot_upload_zip(builder, package_files) - - -def pack_linux(builder): - blender_executable = os.path.join(builder.install_dir, 'blender') - - info = buildbot_utils.VersionInfo(builder) - - # Strip all unused symbols from the binaries - print("Stripping binaries...") - buildbot_utils.call(builder.command_prefix + ['strip', '--strip-all', blender_executable]) - - print("Stripping python...") - py_target = os.path.join(builder.install_dir, info.short_version) - buildbot_utils.call( - builder.command_prefix + [ - 'find', py_target, '-iname', '*.so', '-exec', 'strip', '-s', '{}', ';', - ], - ) - - # Construct package name - platform_name = 'linux64' - package_name = get_package_name(builder, platform_name) - package_filename = package_name + ".tar.xz" - - print("Creating .tar.xz archive") - package_filepath = builder.install_dir + '.tar.xz' - create_tar_xz(builder.install_dir, package_filepath, package_name) - - # Create buildbot_upload.zip - create_buildbot_upload_zip(builder, [(package_filepath, package_filename)]) - - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - - # Make sure install directory always exists - os.makedirs(builder.install_dir, exist_ok=True) - - if builder.platform == 'mac': - pack_mac(builder) - elif builder.platform == 'win': - pack_win(builder) - elif builder.platform == 'linux': - pack_linux(builder) diff --git a/build_files/buildbot/worker_test.py b/build_files/buildbot/worker_test.py deleted file mode 100644 index 8a6e7d4bb69..00000000000 --- a/build_files/buildbot/worker_test.py +++ /dev/null @@ -1,42 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import buildbot_utils -import os -import sys - - -def get_ctest_arguments(builder): - args = ['--output-on-failure'] - if builder.platform == 'win': - args += ['-C', 'Release'] - return args - - -def test(builder): - os.chdir(builder.build_dir) - - command = builder.command_prefix + ['ctest'] + get_ctest_arguments(builder) - buildbot_utils.call(command) - - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - test(builder) diff --git a/build_files/buildbot/worker_update.py b/build_files/buildbot/worker_update.py deleted file mode 100644 index 36a7ae31c84..00000000000 --- a/build_files/buildbot/worker_update.py +++ /dev/null @@ -1,31 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import buildbot_utils -import os -import sys - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - os.chdir(builder.blender_dir) - - # Run make update which handles all libraries and submodules. - make_update = os.path.join(builder.blender_dir, "build_files", "utils", "make_update.py") - buildbot_utils.call([sys.executable, make_update, '--no-blender', "--use-tests", "--use-centos-libraries"]) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index d4fd3779665..75aafd45c82 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -56,10 +56,6 @@ set(WITH_TBB ON CACHE BOOL "" FORCE) set(WITH_USD ON CACHE BOOL "" FORCE) set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE) -set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE) -set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE) -set(CYCLES_CUDA_BINARIES_ARCH sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm_75;sm_86;compute_75 CACHE STRING "" FORCE) -set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE) # platform dependent options if(APPLE) @@ -80,4 +76,8 @@ if(UNIX AND NOT APPLE) endif() if(NOT APPLE) set(WITH_XR_OPENXR ON CACHE BOOL "" FORCE) + + set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE) + set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE) + set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE) endif() diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 813ac013cdf..5ae5ed7c29e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -596,14 +596,6 @@ function(SETUP_LIBDIRS) link_directories(${GMP_LIBPATH}) endif() - if(WITH_GHOST_WAYLAND) - link_directories( - ${wayland-client_LIBRARY_DIRS} - ${wayland-egl_LIBRARY_DIRS} - ${xkbcommon_LIBRARY_DIRS} - ${wayland-cursor_LIBRARY_DIRS}) - endif() - if(WIN32 AND NOT UNIX) link_directories(${PTHREADS_LIBPATH}) endif() diff --git a/build_files/cmake/packaging.cmake b/build_files/cmake/packaging.cmake index 4a0a4f2493d..265b3c0e2ab 100644 --- a/build_files/cmake/packaging.cmake +++ b/build_files/cmake/packaging.cmake @@ -104,8 +104,8 @@ if(WIN32) set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium) endif() -set(CPACK_PACKAGE_EXECUTABLES "blender" "blender") -set(CPACK_CREATE_DESKTOP_LINKS "blender" "blender") +set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "blender") +set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "blender") include(CPack) diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 7791112c6ab..fcfde5a07ba 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -575,17 +575,17 @@ if(WITH_GHOST_WAYLAND) pkg_check_modules(wayland-scanner REQUIRED wayland-scanner) pkg_check_modules(xkbcommon REQUIRED xkbcommon) pkg_check_modules(wayland-cursor REQUIRED wayland-cursor) + pkg_check_modules(dbus REQUIRED dbus-1) set(WITH_GL_EGL ON) - if(WITH_GHOST_WAYLAND) - list(APPEND PLATFORM_LINKLIBS - ${wayland-client_LIBRARIES} - ${wayland-egl_LIBRARIES} - ${xkbcommon_LIBRARIES} - ${wayland-cursor_LIBRARIES} - ) - endif() + list(APPEND PLATFORM_LINKLIBS + ${wayland-client_LINK_LIBRARIES} + ${wayland-egl_LINK_LIBRARIES} + ${xkbcommon_LINK_LIBRARIES} + ${wayland-cursor_LINK_LIBRARIES} + ${dbus_LINK_LIBRARIES} + ) endif() if(WITH_GHOST_X11) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index b6ec016aa95..fe5b179d6d1 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -119,7 +119,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099") list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi - pathcch + pathcch Shcore ) if(WITH_INPUT_IME) @@ -144,8 +144,8 @@ add_definitions(-D_ALLOW_KEYWORD_MACROS) # that both /GR and /GR- are specified. remove_cc_flag("/GR") -# We want to support Windows 7 level ABI -add_definitions(-D_WIN32_WINNT=0x601) +# Make the Windows 8.1 API available for use. +add_definitions(-D_WIN32_WINNT=0x603) include(build_files/cmake/platform/platform_win32_bundle_crt.cmake) remove_cc_flag("/MDd" "/MD" "/Zi") @@ -675,7 +675,7 @@ if(WITH_SYSTEM_AUDASPACE) endif() if(WITH_TBB) - set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/debug/tbb_debug.lib) + set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) if(WITH_TBB_MALLOC_PROXY) diff --git a/build_files/cmake/platform/platform_win32_bundle_crt.cmake b/build_files/cmake/platform/platform_win32_bundle_crt.cmake index f5bc024e4e0..7b2e1edb1b3 100644 --- a/build_files/cmake/platform/platform_win32_bundle_crt.cmake +++ b/build_files/cmake/platform/platform_win32_bundle_crt.cmake @@ -15,6 +15,15 @@ if(WITH_WINDOWS_BUNDLE_CRT) include(InstallRequiredSystemLibraries) + # ucrtbase(d).dll cannot be in the manifest, due to the way windows 10 handles + # redirects for this dll, for details see T88813. + foreach(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}) + string(FIND ${lib} "ucrtbase" pos) + if(NOT pos EQUAL -1) + list(REMOVE_ITEM CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${lib}) + install(FILES ${lib} DESTINATION . COMPONENT Libraries) + endif() + endforeach() # Install the CRT to the blender.crt Sub folder. install(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ./blender.crt COMPONENT Libraries) diff --git a/build_files/config/README.md b/build_files/config/README.md new file mode 100644 index 00000000000..6c429e4e58d --- /dev/null +++ b/build_files/config/README.md @@ -0,0 +1,8 @@ +Pipeline Config +=============== + +This configuration file is used by buildbot new pipeline for the `update-code` step. + +It will soon be used by the ../utils/make_update.py script. + +Both buildbot and developers will eventually use the same configuration file. diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json new file mode 100644 index 00000000000..47318b2f7dc --- /dev/null +++ b/build_files/config/pipeline_config.json @@ -0,0 +1,87 @@ +{ + "update-code": + { + "git" : + { + "submodules": + [ + { "path": "release/scripts/addons", "branch": "master", "commit_id": "HEAD" }, + { "path": "release/scripts/addons_contrib", "branch": "master", "commit_id": "HEAD" }, + { "path": "release/datafiles/locale", "branch": "master", "commit_id": "HEAD" }, + { "path": "source/tools", "branch": "master", "commit_id": "HEAD" } + ] + }, + "svn": + { + "tests": { "path": "lib/tests", "branch": "trunk", "commit_id": "HEAD" }, + "libraries": + { + "darwin-x86_64": { "path": "lib/darwin", "branch": "trunk", "commit_id": "HEAD" }, + "darwin-arm64": { "path": "lib/darwin_arm64", "branch": "trunk", "commit_id": "HEAD" }, + "linux-x86_64": { "path": "lib/linux_centos7_x86_64", "branch": "trunk", "commit_id": "HEAD" }, + "windows-amd64": { "path": "lib/win64_vc15", "branch": "trunk", "commit_id": "HEAD" } + } + } + }, + "buildbot": + { + "gcc": + { + "version": "9.0" + }, + "sdks": + { + "optix": + { + "version": "7.1.0" + }, + "cuda10": + { + "version": "10.1" + }, + "cuda11": + { + "version": "11.3" + } + }, + "cmake": + { + "default": + { + "version": "any", + "overrides": + { + + } + }, + "darwin-x86_64": + { + "overrides": + { + + } + }, + "darwin-arm64": + { + "overrides": + { + + } + }, + "linux-x86_64": + { + "overrides": + { + + } + }, + "windows-amd64": + { + "overrides": + { + + } + } + } + } +} diff --git a/build_files/utils/README.md b/build_files/utils/README.md new file mode 100644 index 00000000000..e78d05b0c69 --- /dev/null +++ b/build_files/utils/README.md @@ -0,0 +1,5 @@ +Make Utility Scripts +==================== + +Scripts used only by developers for now + diff --git a/doc/blender_file_format/BlendFileReader.py b/doc/blender_file_format/BlendFileReader.py index d4aed722578..a2f214bc4fc 100644 --- a/doc/blender_file_format/BlendFileReader.py +++ b/doc/blender_file_format/BlendFileReader.py @@ -85,7 +85,7 @@ def openBlendFile(filename): ''' handle = open(filename, 'rb') magic = ReadString(handle, 7) - if magic in ("BLENDER", "BULLETf"): + if magic in {"BLENDER", "BULLETf"}: log.debug("normal blendfile detected") handle.seek(0, os.SEEK_SET) return handle @@ -137,7 +137,7 @@ class BlendFile: fileblock = BlendFileBlock(handle, self) found_dna_block = False while not found_dna_block: - if fileblock.Header.Code in ("DNA1", "SDNA"): + if fileblock.Header.Code in {"DNA1", "SDNA"}: self.Catalog = DNACatalog(self.Header, handle) found_dna_block = True else: diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 032f8a86bd5..4be27e0f0e8 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -954,7 +954,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): # constant, not much fun we can do here except to list it. # TODO, figure out some way to document these! fw(".. data:: %s\n\n" % attribute) - write_indented_lines(" ", fw, "constant value %s" % repr(value), False) + write_indented_lines(" ", fw, "Constant value %s" % repr(value), False) fw("\n") else: BPY_LOGGER.debug("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__)) @@ -1246,7 +1246,7 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False): "%s.\n" % ( identifier, # Account for multi-line enum descriptions, allowing this to be a block of text. - indent(", ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), + indent(" -- ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), ) for identifier, name, description in prop.enum_items ]) diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp index 2cd3261bd20..10517d1d596 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp @@ -75,6 +75,7 @@ void FFMPEGWriter::encode() m_frame->nb_samples = m_input_samples; m_frame->format = m_codecCtx->sample_fmt; m_frame->channel_layout = m_codecCtx->channel_layout; + m_frame->channels = m_specs.channels; if(avcodec_fill_audio_frame(m_frame, m_specs.channels, m_codecCtx->sample_fmt, reinterpret_cast<data_t*>(data), m_input_buffer.getSize(), 0) < 0) AUD_THROW(FileException, "File couldn't be written, filling the audio frame failed with ffmpeg."); diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index b31841801d8..6954c5c2f26 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -83,6 +83,8 @@ struct BlenderCamera { BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + float passepartout_alpha; + Transform matrix; float offscreen_dicing_scale; @@ -125,6 +127,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -212,6 +215,8 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->lens = b_camera.lens(); + bcam->passepartout_alpha = b_camera.show_passepartout() ? b_camera.passepartout_alpha() : 0.0f; + if (b_camera.dof().use_dof()) { /* allow f/stop number to change aperture_size but still * give manual control over aperture radius */ @@ -834,15 +839,19 @@ static void blender_camera_border(BlenderCamera *bcam, full_border, &bcam->viewport_camera_border); - if (!b_render.use_border()) { + if (b_render.use_border()) { + bcam->border.left = b_render.border_min_x(); + bcam->border.right = b_render.border_max_x(); + bcam->border.bottom = b_render.border_min_y(); + bcam->border.top = b_render.border_max_y(); + } + else if (bcam->passepartout_alpha == 1.0f) { + bcam->border = full_border; + } + else { return; } - bcam->border.left = b_render.border_min_x(); - bcam->border.right = b_render.border_max_x(); - bcam->border.bottom = b_render.border_min_y(); - bcam->border.top = b_render.border_max_y(); - /* Determine viewport subset matching camera border. */ blender_camera_border_subset(b_engine, b_render, @@ -885,8 +894,7 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, +BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, @@ -902,7 +910,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, if (b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA) use_border = b_v3d.use_render_border(); else - use_border = b_render.use_border(); + /* the camera can always have a passepartout */ + use_border = true; if (use_border) { /* border render */ diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 89854f6a0e5..29de886e4ff 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -155,7 +155,7 @@ void BlenderSession::create_session() /* set buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); session->reset(buffer_params, session_params.samples); b_engine.use_highlight_tiles(session_params.progressive_refine == false); @@ -242,8 +242,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, - b_null_space_view3d, + BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, b_null_region_view3d, scene->camera, width, @@ -486,7 +485,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background, b_view_layer); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); /* temporary render result to find needed passes and views */ BL::RenderResult b_rr = begin_render_result( @@ -810,7 +809,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* get buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); if (!buffer_params.denoising_data_pass) { session_params.denoising.use = false; @@ -889,7 +888,7 @@ bool BlenderSession::draw(int w, int h) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { @@ -907,7 +906,7 @@ bool BlenderSession::draw(int w, int h) /* draw */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); DeviceDrawParams draw_params; if (session->params.display_buffer_linear) { diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 736c10e8881..7f129310736 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1373,7 +1373,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, BlenderViewportParameters new_viewport_parameters(b_v3d); if (world_recalc || update_all || b_world.ptr.data != world_map || - viewport_parameters.modified(new_viewport_parameters)) { + viewport_parameters.shader_modified(new_viewport_parameters)) { Shader *shader = scene->default_background; ShaderGraph *graph = new ShaderGraph(); @@ -1501,8 +1501,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_transparent_roughness_threshold(0.0f); } - background->set_use_shader(view_layer.use_background_shader | - viewport_parameters.custom_viewport_parameters()); + background->set_use_shader(view_layer.use_background_shader || + viewport_parameters.use_custom_shader()); background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao); background->tag_update(scene); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 9d0f9f29f94..82b3abd4432 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -224,9 +224,13 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d if (b_v3d) { BlenderViewportParameters new_viewport_parameters(b_v3d); - if (viewport_parameters.modified(new_viewport_parameters)) { + + if (viewport_parameters.shader_modified(new_viewport_parameters)) { world_recalc = true; + has_updates_ = true; } + + has_updates_ |= viewport_parameters.modified(new_viewport_parameters); } } @@ -246,7 +250,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); - sync_view_layer(b_v3d, b_view_layer); + sync_view_layer(b_view_layer); sync_integrator(); sync_film(b_v3d); sync_shaders(b_depsgraph, b_v3d); @@ -441,7 +445,7 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) /* Render Layer */ -void BlenderSync::sync_view_layer(BL::SpaceView3D & /*b_v3d*/, BL::ViewLayer &b_view_layer) +void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) { view_layer.name = b_view_layer.name(); diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 15a10f2b46b..1c98e529190 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -73,7 +73,7 @@ class BlenderSync { int width, int height, void **python_thread_state); - void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer); + void sync_view_layer(BL::ViewLayer &b_view_layer); vector<Pass> sync_render_passes(BL::Scene &b_scene, BL::RenderLayer &b_render_layer, BL::ViewLayer &b_view_layer, @@ -104,8 +104,7 @@ class BlenderSync { bool background, BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, + static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 73ef5f94720..07408fee218 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "blender_viewport.h" #include "blender_util.h" @@ -25,29 +26,39 @@ BlenderViewportParameters::BlenderViewportParameters() studiolight_rotate_z(0.0f), studiolight_intensity(1.0f), studiolight_background_alpha(1.0f), - studiolight_path(ustring()) + display_pass(PASS_COMBINED) { } BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) : BlenderViewportParameters() { - /* We only copy the parameters if we are in look dev mode. otherwise + if (!b_v3d) { + return; + } + + BL::View3DShading shading = b_v3d.shading(); + PointerRNA cshading = RNA_pointer_get(&shading.ptr, "cycles"); + + /* We only copy the shading parameters if we are in look dev mode. otherwise * defaults are being used. These defaults mimic normal render settings */ - if (b_v3d && b_v3d.shading().type() == BL::View3DShading::type_RENDERED) { - use_scene_world = b_v3d.shading().use_scene_world_render(); - use_scene_lights = b_v3d.shading().use_scene_lights_render(); + if (shading.type() == BL::View3DShading::type_RENDERED) { + use_scene_world = shading.use_scene_world_render(); + use_scene_lights = shading.use_scene_lights_render(); + if (!use_scene_world) { - studiolight_rotate_z = b_v3d.shading().studiolight_rotate_z(); - studiolight_intensity = b_v3d.shading().studiolight_intensity(); - studiolight_background_alpha = b_v3d.shading().studiolight_background_alpha(); - studiolight_path = b_v3d.shading().selected_studio_light().path(); + studiolight_rotate_z = shading.studiolight_rotate_z(); + studiolight_intensity = shading.studiolight_intensity(); + studiolight_background_alpha = shading.studiolight_background_alpha(); + studiolight_path = shading.selected_studio_light().path(); } } + + /* Film. */ + display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); } -/* Check if two instances are different. */ -const bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const +bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const { return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights || studiolight_rotate_z != other.studiolight_rotate_z || @@ -56,26 +67,26 @@ const bool BlenderViewportParameters::modified(const BlenderViewportParameters & studiolight_path != other.studiolight_path; } -const bool BlenderViewportParameters::custom_viewport_parameters() const +bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const { - return !(use_scene_world && use_scene_lights); + return display_pass != other.display_pass; } -PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d) +bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const { - PassType display_pass = PASS_NONE; - if (b_v3d) { - BL::View3DShading b_view3dshading = b_v3d.shading(); - PointerRNA cshading = RNA_pointer_get(&b_view3dshading.ptr, "cycles"); - display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); - } - return display_pass; + return shader_modified(other) || film_modified(other); +} + +bool BlenderViewportParameters::use_custom_shader() const +{ + return !(use_scene_world && use_scene_lights); } PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes) { if (b_v3d) { - PassType display_pass = BlenderViewportParameters::get_viewport_display_render_pass(b_v3d); + const BlenderViewportParameters viewport_parameters(b_v3d); + const PassType display_pass = viewport_parameters.display_pass; passes.clear(); Pass::add(display_pass, passes); diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index 7c6c9c4d274..d6518597053 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -18,17 +18,18 @@ #define __BLENDER_VIEWPORT_H__ #include "MEM_guardedalloc.h" + #include "RNA_access.h" #include "RNA_blender_cpp.h" #include "RNA_types.h" #include "render/film.h" -#include "util/util_param.h" CCL_NAMESPACE_BEGIN class BlenderViewportParameters { - private: + public: + /* Shader. */ bool use_scene_world; bool use_scene_lights; float studiolight_rotate_z; @@ -36,17 +37,24 @@ class BlenderViewportParameters { float studiolight_background_alpha; ustring studiolight_path; + /* Film. */ + PassType display_pass; + BlenderViewportParameters(); - BlenderViewportParameters(BL::SpaceView3D &b_v3d); + explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); - const bool modified(const BlenderViewportParameters &other) const; - const bool custom_viewport_parameters() const; - friend class BlenderSync; + /* Check whether any of shading related settings are different from the given parameters. */ + bool shader_modified(const BlenderViewportParameters &other) const; - public: - /* Retrieve the render pass that needs to be displayed on the given `SpaceView3D` - * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ - static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d); + /* Check whether any of film related settings are different from the given parameters. */ + bool film_modified(const BlenderViewportParameters &other) const; + + /* Check whether any of settings are different from the given parameters. */ + bool modified(const BlenderViewportParameters &other) const; + + /* Returns truth when a custom shader defined by the viewport is to be used instead of the + * regular background shader or scene light. */ + bool use_custom_shader() const; }; PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes); diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 01de0724cb2..b008dfa376f 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -726,7 +726,11 @@ class OptiXDevice : public CUDADevice { } } else if (task.type == DeviceTask::SHADER) { - launch_shader_eval(task, thread_index); + // CUDA kernels are used when doing baking + if (optix_module == NULL) + CUDADevice::shader(task); + else + launch_shader_eval(task, thread_index); } else if (task.type == DeviceTask::DENOISE_BUFFER) { // Set up a single tile that covers the whole task and denoise it diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index ba25c0e24e4..ce37bd0b15e 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -195,31 +195,108 @@ ccl_device float2 regular_polygon_sample(float corners, float rotation, float u, ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) { - float3 R; - float NI = dot(N, I); - float NgR, threshold; - - /* Check if the incident ray is coming from behind normal N. */ - if (NI > 0) { - /* Normal reflection */ - R = (2 * NI) * N - I; - NgR = dot(Ng, R); - - /* Reflection rays may always be at least as shallow as the incoming ray. */ - threshold = min(0.9f * dot(Ng, I), 0.01f); - if (NgR >= threshold) { - return N; + float3 R = 2 * dot(N, I) * N - I; + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9f * dot(Ng, I), 0.01f); + if (dot(Ng, R) >= threshold) { + return N; + } + + /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. + * The X axis is found by normalizing the component of N that's orthogonal to Ng. + * The Y axis isn't actually needed. + */ + float NdotNg = dot(N, Ng); + float3 X = normalize(N - NdotNg * Ng); + + /* Keep math expressions. */ + /* clang-format off */ + /* Calculate N.z and N.x in the local coordinate system. + * + * The goal of this computation is to find a N' that is rotated towards Ng just enough + * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. + * + * According to the standard reflection equation, + * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. + * + * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get + * 2*dot(N', I)*N'.z - I.z = t. + * + * The rotation is simple to express in the coordinate system we formed - + * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, + * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . + * + * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). + * + * With these simplifications, + * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. + * + * The only unknown here is N'.z, so we can solve for that. + * + * The equation has four solutions in general: + * + * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) + * We can simplify this expression a bit by grouping terms: + * + * a = I.x^2 + I.z^2 + * b = sqrt(I.x^2 * (a - t^2)) + * c = I.z*t + a + * N'.z = +-sqrt(0.5*(+-b + c)/a) + * + * Two solutions can immediately be discarded because they're negative so N' would lie in the + * lower hemisphere. + */ + /* clang-format on */ + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + /* Evaluate both solutions. + * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than + * one), so check for that first. If no option is viable (might happen in extreme cases like N + * being in the wrong hemisphere), give up and return Ng. */ + float fac = 0.5f / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); + bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); + + float2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); + float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); + + float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5f); + valid2 = (R2 >= 1e-5f); + if (valid1 && valid2) { + /* If both solutions are valid, return the one with the shallower reflection since it will be + * closer to the input (if the original reflection wasn't shallow, we would not be in this + * part of the function). */ + N_new = (R1 < R2) ? N1 : N2; } + else { + /* If only one reflection is valid (= positive), pick that one. */ + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + /* Only one solution passes the N'.z criterium, so pick that one. */ + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); } else { - /* Bad incident */ - R = -I; - NgR = dot(Ng, R); - threshold = 0.01f; + return Ng; } - R = R + Ng * (threshold - NgR); /* Lift the reflection above the threshold. */ - return normalize(I * len(R) + R * len(I)); /* Find a bisector. */ + return N_new.x * X + N_new.y * Ng; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/shaders/node_vector_math.osl b/intern/cycles/kernel/shaders/node_vector_math.osl index 3963c23ea9c..c08d75b99ef 100644 --- a/intern/cycles/kernel/shaders/node_vector_math.osl +++ b/intern/cycles/kernel/shaders/node_vector_math.osl @@ -52,6 +52,9 @@ shader node_vector_math(string math_type = "add", else if (math_type == "faceforward") { Vector = compatible_faceforward(Vector1, Vector2, Vector3); } + else if (math_type == "multiply_add") { + Vector = Vector1 * Vector2 + Vector3; + } else if (math_type == "dot_product") { Value = dot(Vector1, Vector2); } diff --git a/intern/cycles/kernel/shaders/stdcycles.h b/intern/cycles/kernel/shaders/stdcycles.h index af7b645d9a2..dd604da68ce 100644 --- a/intern/cycles/kernel/shaders/stdcycles.h +++ b/intern/cycles/kernel/shaders/stdcycles.h @@ -84,30 +84,67 @@ closure color principled_hair(normal N, closure color henyey_greenstein(float g) BUILTIN; closure color absorption() BUILTIN; -normal ensure_valid_reflection(normal Ng, normal I, normal N) +normal ensure_valid_reflection(normal Ng, vector I, normal N) { /* The implementation here mirrors the one in kernel_montecarlo.h, * check there for an explanation of the algorithm. */ - vector R; - float NI = dot(N, I); - float NgR, threshold; - - if (NI > 0) { - R = (2 * NI) * N - I; - NgR = dot(Ng, R); - threshold = min(0.9 * dot(Ng, I), 0.01); - if (NgR >= threshold) { - return N; + + float sqr(float x) + { + return x * x; + } + + vector R = 2 * dot(N, I) * N - I; + + float threshold = min(0.9 * dot(Ng, I), 0.01); + if (dot(Ng, R) >= threshold) { + return N; + } + + float NdotNg = dot(N, Ng); + vector X = normalize(N - NdotNg * Ng); + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = sqrt(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + float fac = 0.5 / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + int valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); + int valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); + + float N_new_x, N_new_z; + if (valid1 && valid2) { + float N1_x = sqrt(1.0 - N1_z2), N1_z = sqrt(N1_z2); + float N2_x = sqrt(1.0 - N2_z2), N2_z = sqrt(N2_z2); + + float R1 = 2 * (N1_x * Ix + N1_z * Iz) * N1_z - Iz; + float R2 = 2 * (N2_x * Ix + N2_z * Iz) * N2_z - Iz; + + valid1 = (R1 >= 1e-5); + valid2 = (R2 >= 1e-5); + if (valid1 && valid2) { + N_new_x = (R1 < R2) ? N1_x : N2_x; + N_new_z = (R1 < R2) ? N1_z : N2_z; + } + else { + N_new_x = (R1 > R2) ? N1_x : N2_x; + N_new_z = (R1 > R2) ? N1_z : N2_z; } } + else if (valid1 || valid2) { + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new_x = sqrt(1.0 - Nz2); + N_new_z = sqrt(Nz2); + } else { - R = -I; - NgR = dot(Ng, R); - threshold = 0.01; + return Ng; } - R = R + Ng * (threshold - NgR); - return normalize(I * length(R) + R * length(I)); + return N_new_x * X + N_new_z * Ng; } #endif /* CCL_STDOSL_H */ diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index dda2e50f916..733ea28f9e5 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -58,7 +58,8 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, float3 vector; /* 3 Vector Operators */ - if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD) { + if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD || + type == NODE_VECTOR_MATH_MULTIPLY_ADD) { uint4 extra_node = read_node(kg, offset); c = stack_load_float3(stack, extra_node.x); } diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index 19fb1da5a1f..9e654f2247f 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -52,6 +52,9 @@ ccl_device void svm_vector_math(float *value, case NODE_VECTOR_MATH_FACEFORWARD: *vector = faceforward(a, b, c); break; + case NODE_VECTOR_MATH_MULTIPLY_ADD: + *vector = a * b + c; + break; case NODE_VECTOR_MATH_DOT_PRODUCT: *value = dot(a, b); break; diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 64a8f82a094..062afcfa5ac 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -341,6 +341,7 @@ typedef enum NodeVectorMathType { NODE_VECTOR_MATH_TANGENT, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD, } NodeVectorMathType; typedef enum NodeClampType { diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index cf345ee075d..dcb456dc1ce 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -654,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, list<Attribute>::iterator it; for (it = attributes.attributes.begin(); it != attributes.attributes.end();) { if (cached_attributes.find(&(*it)) == cached_attributes.end()) { - attributes.attributes.erase(it++); - attributes.modified = true; + attributes.remove(it++); continue; } diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index 4d09c40b07b..33ce3502879 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -606,7 +606,8 @@ void read_geometry_data(AlembicProcedural *proc, template<typename T> struct value_type_converter { using cycles_type = float; - static constexpr TypeDesc type_desc = TypeFloat; + /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */ + static constexpr TypeDesc type_desc = TypeDesc::FLOAT; static constexpr const char *type_name = "float (default)"; static cycles_type convert_value(T value) diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index d6a638fd4cd..bf9d69cb47e 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name) return ATTR_STD_NONE; } +AttrKernelDataType Attribute::kernel_type(const Attribute &attr) +{ + if (attr.element == ATTR_ELEMENT_CORNER) { + return AttrKernelDataType::UCHAR4; + } + + if (attr.type == TypeDesc::TypeFloat) { + return AttrKernelDataType::FLOAT; + } + + if (attr.type == TypeFloat2) { + return AttrKernelDataType::FLOAT2; + } + + return AttrKernelDataType::FLOAT3; +} + void Attribute::get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const @@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom, /* Attribute Set */ AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim) - : geometry(geometry), prim(prim) + : modified_flag(~0u), geometry(geometry), prim(prim) { } @@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme Attribute new_attr(name, type, element, geometry, prim); attributes.emplace_back(std::move(new_attr)); - modified = true; + tag_modified(attributes.back()); return &attributes.back(); } @@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute) } } +void AttributeSet::remove(list<Attribute>::iterator it) +{ + tag_modified(*it); + attributes.erase(it); +} + void AttributeSet::resize(bool reserve_only) { foreach (Attribute &attr, attributes) { @@ -674,15 +695,13 @@ void AttributeSet::update(AttributeSet &&new_attributes) for (it = attributes.begin(); it != attributes.end();) { if (it->std != ATTR_STD_NONE) { if (new_attributes.find(it->std) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } else if (it->name != "") { if (new_attributes.find(it->name) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } @@ -699,7 +718,27 @@ void AttributeSet::clear_modified() foreach (Attribute &attr, attributes) { attr.modified = false; } - modified = false; + + modified_flag = 0; +} + +void AttributeSet::tag_modified(const Attribute &attr) +{ + /* Some attributes are not stored in the various kernel attribute arrays + * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard + * corresponds to an attribute which will be stored in the kernel's attribute arrays. */ + const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL && + attr.std != ATTR_STD_VERTEX_NORMAL); + + if (modifies_device_array) { + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + modified_flag |= (1u << kernel_type); + } +} + +bool AttributeSet::modified(AttrKernelDataType kernel_type) const +{ + return (modified_flag & (1u << kernel_type)) != 0; } /* AttributeRequest */ diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index 18c9e5ab83a..004c267cabc 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -39,6 +39,21 @@ class Hair; class Mesh; struct Transform; +/* AttrKernelDataType. + * + * The data type of the device arrays storing the attribute's data. Those data types are different + * than the ones for attributes as some attribute types are stored in the same array, e.g. Point, + * Vector, and Transform are all stored as float3 in the kernel. + * + * The values of this enumeration are also used as flags to detect changes in AttributeSet. */ + +enum AttrKernelDataType { + FLOAT = 0, + FLOAT2 = 1, + FLOAT3 = 2, + UCHAR4 = 3, +}; + /* Attribute * * Arbitrary data layers on meshes. @@ -167,6 +182,8 @@ class Attribute { static const char *standard_name(AttributeStandard std); static AttributeStandard name_standard(const char *name); + static AttrKernelDataType kernel_type(const Attribute &attr); + void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const; }; @@ -175,11 +192,12 @@ class Attribute { * Set of attributes on a mesh. */ class AttributeSet { + uint32_t modified_flag; + public: Geometry *geometry; AttributePrimitive prim; list<Attribute> attributes; - bool modified = true; AttributeSet(Geometry *geometry, AttributePrimitive prim); AttributeSet(AttributeSet &&) = default; @@ -197,6 +215,8 @@ class AttributeSet { void remove(Attribute *attribute); + void remove(list<Attribute>::iterator it); + void resize(bool reserve_only = false); void clear(bool preserve_voxel_data = false); @@ -204,7 +224,18 @@ class AttributeSet { * and remove any attribute not found on the new set from this. */ void update(AttributeSet &&new_attributes); + /* Return whether the attributes of the given kernel_type are modified, where "modified" means + * that some attributes of the given type were added or removed from this AttributeSet. This does + * not mean that the data of the remaining attributes in this AttributeSet were also modified. To + * check this, use Attribute.modified. */ + bool modified(AttrKernelDataType kernel_type) const; + void clear_modified(); + + private: + /* Set the relevant modified flag for the attribute. Only attributes that are stored in device + * arrays will be considered for tagging this AttributeSet as modified. */ + void tag_modified(const Attribute &attr); }; /* AttributeRequest diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 16fc36231b4..ce76658acb6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -830,10 +830,13 @@ void GeometryManager::device_update_attributes(Device *device, dscene->attributes_float3.alloc(attr_float3_size); dscene->attributes_uchar4.alloc(attr_uchar4_size); - const bool copy_all_data = dscene->attributes_float.need_realloc() || - dscene->attributes_float2.need_realloc() || - dscene->attributes_float3.need_realloc() || - dscene->attributes_uchar4.need_realloc(); + /* The order of those flags needs to match that of AttrKernelDataType. */ + const bool attributes_need_realloc[4] = { + dscene->attributes_float.need_realloc(), + dscene->attributes_float2.need_realloc(), + dscene->attributes_float3.need_realloc(), + dscene->attributes_uchar4.need_realloc(), + }; size_t attr_float_offset = 0; size_t attr_float2_offset = 0; @@ -852,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device, if (attr) { /* force a copy if we need to reallocate all the data */ - attr->modified |= copy_all_data; + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } update_attribute_element_offset(geom, @@ -875,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device, if (subd_attr) { /* force a copy if we need to reallocate all the data */ - subd_attr->modified |= copy_all_data; + subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } update_attribute_element_offset(mesh, @@ -906,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = values.find(req); + if (attr) { + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; + } + update_attribute_element_offset(object->geometry, dscene->attributes_float, attr_float_offset, @@ -941,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device, /* copy to device */ progress.set_status("Updating Mesh", "Copying Attributes to device"); - dscene->attributes_float.copy_to_device(); - dscene->attributes_float2.copy_to_device(); - dscene->attributes_float3.copy_to_device(); - dscene->attributes_uchar4.copy_to_device(); + dscene->attributes_float.copy_to_device_if_modified(); + dscene->attributes_float2.copy_to_device_if_modified(); + dscene->attributes_float3.copy_to_device_if_modified(); + dscene->attributes_uchar4.copy_to_device_if_modified(); if (progress.get_cancel()) return; @@ -1431,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags, continue; } - if (attr.element == ATTR_ELEMENT_CORNER) { - device_update_flags |= ATTR_UCHAR4_MODIFIED; - } - else if (attr.type == TypeDesc::TypeFloat) { - device_update_flags |= ATTR_FLOAT_MODIFIED; - } - else if (attr.type == TypeFloat2) { - device_update_flags |= ATTR_FLOAT2_MODIFIED; - } - else if (attr.type == TypeDesc::TypeMatrix) { - device_update_flags |= ATTR_FLOAT3_MODIFIED; - } - else if (attr.element != ATTR_ELEMENT_VOXEL) { - device_update_flags |= ATTR_FLOAT3_MODIFIED; + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + + switch (kernel_type) { + case AttrKernelDataType::FLOAT: { + device_update_flags |= ATTR_FLOAT_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT2: { + device_update_flags |= ATTR_FLOAT2_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT3: { + device_update_flags |= ATTR_FLOAT3_MODIFIED; + break; + } + case AttrKernelDataType::UCHAR4: { + device_update_flags |= ATTR_UCHAR4_MODIFIED; + break; + } } } } +static void update_attribute_realloc_flags(uint32_t &device_update_flags, + const AttributeSet &attributes) +{ + if (attributes.modified(AttrKernelDataType::FLOAT)) { + device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT2)) { + device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT3)) { + device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::UCHAR4)) { + device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC; + } +} + void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) { if (!need_update() && !need_flags_update) { @@ -1471,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro foreach (Geometry *geom, scene->geometry) { geom->has_volume = false; - if (geom->attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, geom->attributes); if (geom->is_mesh()) { Mesh *mesh = static_cast<Mesh *>(geom); - - if (mesh->subd_attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes); } foreach (Node *node, geom->get_used_shaders()) { @@ -2039,7 +2063,7 @@ void GeometryManager::device_update(Device *device, * for meshes with correct bounding boxes. * * This wouldn't cause wrong results, just true - * displacement might be less optimal ot calculate. + * displacement might be less optimal to calculate. */ scene->object_manager->need_flags_update = old_need_object_flags_update; } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index f3d420c6fcb..5792d2458d1 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -6093,6 +6093,7 @@ NODE_DEFINE(VectorMathNode) type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT); type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT); type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD); + type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD); type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT); @@ -6165,7 +6166,8 @@ void VectorMathNode::compile(SVMCompiler &compiler) int vector_stack_offset = compiler.stack_assign_if_linked(vector_out); /* 3 Vector Operators */ - if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD) { + if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD || + math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) { ShaderInput *vector3_in = input("Vector3"); int vector3_stack_offset = compiler.stack_assign(vector3_in); compiler.add_node( diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h index 7c39ed675b5..906bf420756 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/util_task.h @@ -32,7 +32,7 @@ typedef function<void(void)> TaskRunFunction; /* Task Pool * - * Pool of tasks that will be executed by the central TaskScheduler.For each + * Pool of tasks that will be executed by the central TaskScheduler. For each * pool, we can wait for all tasks to be done, or cancel them before they are * done. * @@ -77,7 +77,7 @@ class TaskPool { /* Task Scheduler * - * Central scheduler that holds running threads ready to execute tasks. A singe + * Central scheduler that holds running threads ready to execute tasks. A single * queue holds the task from all pools. */ class TaskScheduler { diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 54a004d53e2..0c22cf82688 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -43,6 +43,55 @@ # define FFMPEG_INLINE static inline #endif +#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \ + ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76)) +# define FFMPEG_USE_DURATION_WORKAROUND 1 + +/* Before ffmpeg 4.4, package duration calculation used depricated variables to calculate the + * packet duration. Use the function from commit + * github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 + * to calculate the correct framerate for ffmpeg < 4.4. + */ + +FFMPEG_INLINE +void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(s, + AV_LOG_WARNING, + "Packet with invalid duration %" PRId64 " in stream %d\n", + pkt->duration, + pkt->stream_index); + pkt->duration = 0; + } + + if (pkt->duration) { + return; + } + + switch (st->codecpar->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) { + pkt->duration = av_rescale_q(1, av_inv_q(st->avg_frame_rate), st->time_base); + } + else if (st->time_base.num * 1000LL > st->time_base.den) { + pkt->duration = 1; + } + break; + case AVMEDIA_TYPE_AUDIO: { + int frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size); + if (frame_size && st->codecpar->sample_rate) { + pkt->duration = av_rescale_q( + frame_size, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + } + break; + } + default: + break; + } +} +#endif + FFMPEG_INLINE void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) { diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index f90e8a973bf..1b5cdb3cda0 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -282,6 +282,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${wayland-egl_INCLUDE_DIRS} ${xkbcommon_INCLUDE_DIRS} ${wayland-cursor_INCLUDE_DIRS} + ${dbus_INCLUDE_DIRS} ) list(APPEND SRC @@ -321,6 +322,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) xdg-shell "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" ) + # xdg-decoration. + generate_protocol_bindings( + xdg-decoration + "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + ) # Pointer-constraints. generate_protocol_bindings( pointer-constraints diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 81d9824ed26..83b9b2ba36b 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -40,6 +40,7 @@ #include <unordered_map> #include <unordered_set> +#include "GHOST_WaylandCursorSettings.h" #include <pointer-constraints-client-protocol.h> #include <relative-pointer-client-protocol.h> #include <wayland-cursor.h> @@ -52,15 +53,6 @@ #include <cstring> -struct output_t { - struct wl_output *output; - int32_t width, height; - int transform; - int scale; - std::string make; - std::string model; -}; - struct buffer_t { void *data; size_t size; @@ -72,6 +64,12 @@ struct cursor_t { struct wl_buffer *buffer; struct wl_cursor_image image; struct buffer_t *file_buffer = nullptr; + struct wl_cursor_theme *theme = nullptr; + int size; + std::string theme_name; + // outputs on which the cursor is visible + std::unordered_set<const output_t *> outputs; + int scale = 1; }; struct data_offer_t { @@ -142,10 +140,14 @@ struct display_t { struct wl_display *display; struct wl_compositor *compositor = nullptr; struct xdg_wm_base *xdg_shell = nullptr; + struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr; struct wl_shm *shm = nullptr; std::vector<output_t *> outputs; std::vector<input_t *> inputs; - struct wl_cursor_theme *cursor_theme = nullptr; + struct { + std::string theme; + int size; + } cursor; struct wl_data_device_manager *data_device_manager = nullptr; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr; struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr; @@ -154,6 +156,8 @@ struct display_t { std::vector<struct wl_egl_window *> os_egl_windows; }; +static GHOST_WindowManager *window_manager = nullptr; + static void display_destroy(display_t *d) { if (d->data_device_manager) { @@ -188,6 +192,9 @@ static void display_destroy(display_t *d) if (input->cursor.surface) { wl_surface_destroy(input->cursor.surface); } + if (input->cursor.theme) { + wl_cursor_theme_destroy(input->cursor.theme); + } if (input->pointer) { wl_pointer_destroy(input->pointer); } @@ -210,10 +217,6 @@ static void display_destroy(display_t *d) delete input; } - if (d->cursor_theme) { - wl_cursor_theme_destroy(d->cursor_theme); - } - if (d->shm) { wl_shm_destroy(d->shm); } @@ -238,6 +241,10 @@ static void display_destroy(display_t *d) wl_compositor_destroy(d->compositor); } + if (d->xdg_decoration_manager) { + zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager); + } + if (d->xdg_shell) { xdg_wm_base_destroy(d->xdg_shell); } @@ -478,7 +485,9 @@ static void dnd_events(const input_t *const input, const GHOST_TEventType event) static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive) { int pipefd[2]; - pipe(pipefd); + if (pipe(pipefd) != 0) { + return {}; + } wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); close(pipefd[1]); @@ -513,7 +522,9 @@ static void data_source_send(void *data, int32_t fd) { const char *const buffer = static_cast<char *>(data); - write(fd, buffer, strlen(buffer) + 1); + if (write(fd, buffer, strlen(buffer) + 1) < 0) { + GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl); + } close(fd); } @@ -789,13 +800,80 @@ static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer) cursor_t *cursor = static_cast<cursor_t *>(data); wl_buffer_destroy(wl_buffer); - cursor->buffer = nullptr; + + if (wl_buffer == cursor->buffer) { + /* the mapped buffer was from a custom cursor */ + cursor->buffer = nullptr; + } } const struct wl_buffer_listener cursor_buffer_listener = { cursor_buffer_release, }; +static GHOST_IWindow *get_window(struct wl_surface *surface) +{ + if (!surface) { + return nullptr; + } + + for (GHOST_IWindow *win : window_manager->getWindows()) { + if (surface == static_cast<const GHOST_WindowWayland *>(win)->surface()) { + return win; + } + } + return nullptr; +} + +static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) +{ + int scale = 0; + for (const output_t *output : cursor.outputs) { + if (output->scale > scale) + scale = output->scale; + } + + if (scale > 0 && cursor.scale != scale) { + cursor.scale = scale; + wl_surface_set_buffer_scale(cursor.surface, scale); + wl_cursor_theme_destroy(cursor.theme); + cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); + return true; + } + return false; +} + +static void cursor_surface_enter(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) +{ + input_t *input = static_cast<input_t *>(data); + for (const output_t *reg_output : input->system->outputs()) { + if (reg_output->output == output) { + input->cursor.outputs.insert(reg_output); + } + } + update_cursor_scale(input->cursor, input->system->shm()); +} + +static void cursor_surface_leave(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) +{ + input_t *input = static_cast<input_t *>(data); + for (const output_t *reg_output : input->system->outputs()) { + if (reg_output->output == output) { + input->cursor.outputs.erase(reg_output); + } + } + update_cursor_scale(input->cursor, input->system->shm()); +} + +struct wl_surface_listener cursor_surface_listener = { + cursor_surface_enter, + cursor_surface_leave, +}; + static void pointer_enter(void *data, struct wl_pointer * /*wl_pointer*/, uint32_t serial, @@ -803,22 +881,28 @@ static void pointer_enter(void *data, wl_fixed_t surface_x, wl_fixed_t surface_y) { - if (!surface) { + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface)); + + if (!win) { return; } + + win->activate(); + input_t *input = static_cast<input_t *>(data); input->pointer_serial = serial; - input->x = wl_fixed_to_int(surface_x); - input->y = wl_fixed_to_int(surface_y); + input->x = win->scale() * wl_fixed_to_int(surface_x); + input->y = win->scale() * wl_fixed_to_int(surface_y); input->focus_pointer = surface; - input->system->pushEvent( - new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)), - input->x, - input->y, - GHOST_TABLET_DATA_NONE)); + win->setCursorShape(win->getCursorShape()); + + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + static_cast<GHOST_WindowWayland *>(win), + input->x, + input->y, + GHOST_TABLET_DATA_NONE)); } static void pointer_leave(void *data, @@ -826,9 +910,14 @@ static void pointer_leave(void *data, uint32_t /*serial*/, struct wl_surface *surface) { - if (surface != nullptr) { - static_cast<input_t *>(data)->focus_pointer = nullptr; + GHOST_IWindow *win = get_window(surface); + + if (!win) { + return; } + + static_cast<input_t *>(data)->focus_pointer = nullptr; + static_cast<GHOST_WindowWayland *>(win)->deactivate(); } static void pointer_motion(void *data, @@ -839,21 +928,20 @@ static void pointer_motion(void *data, { input_t *input = static_cast<input_t *>(data); - GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_pointer)); + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer)); if (!win) { return; } - input->x = wl_fixed_to_int(surface_x); - input->y = wl_fixed_to_int(surface_y); + input->x = win->scale() * wl_fixed_to_int(surface_x); + input->y = win->scale() * wl_fixed_to_int(surface_y); input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), GHOST_kEventCursorMove, win, - wl_fixed_to_int(surface_x), - wl_fixed_to_int(surface_y), + input->x, + input->y, GHOST_TABLET_DATA_NONE)); } @@ -864,6 +952,14 @@ static void pointer_button(void *data, uint32_t button, uint32_t state) { + input_t *input = static_cast<input_t *>(data); + + GHOST_IWindow *win = get_window(input->focus_pointer); + + if (!win) { + return; + } + GHOST_TEventType etype = GHOST_kEventUnknown; switch (state) { case WL_POINTER_BUTTON_STATE_RELEASED: @@ -887,9 +983,6 @@ static void pointer_button(void *data, break; } - input_t *input = static_cast<input_t *>(data); - GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_pointer)); input->data_source->source_serial = serial; input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); input->system->pushEvent(new GHOST_EventButton( @@ -902,12 +995,18 @@ static void pointer_axis(void *data, uint32_t axis, wl_fixed_t value) { + input_t *input = static_cast<input_t *>(data); + + GHOST_IWindow *win = get_window(input->focus_pointer); + + if (!win) { + return; + } + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { return; } - input_t *input = static_cast<input_t *>(data); - GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( - wl_surface_get_user_data(input->focus_pointer)); + input->system->pushEvent( new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1)); } @@ -1137,7 +1236,12 @@ static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capa input->cursor.visible = true; input->cursor.buffer = nullptr; input->cursor.file_buffer = new buffer_t; + if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { + input->cursor.theme_name = std::string(); + input->cursor.size = default_cursor_size; + } wl_pointer_add_listener(input->pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { @@ -1160,8 +1264,8 @@ static void output_geometry(void *data, struct wl_output * /*wl_output*/, int32_t /*x*/, int32_t /*y*/, - int32_t /*physical_width*/, - int32_t /*physical_height*/, + int32_t physical_width, + int32_t physical_height, int32_t /*subpixel*/, const char *make, const char *model, @@ -1171,6 +1275,8 @@ static void output_geometry(void *data, output->transform = transform; output->make = std::string(make); output->model = std::string(model); + output->width_mm = physical_width; + output->height_mm = physical_height; } static void output_mode(void *data, @@ -1181,8 +1287,8 @@ static void output_mode(void *data, int32_t /*refresh*/) { output_t *output = static_cast<output_t *>(data); - output->width = width; - output->height = height; + output->width_pxl = width; + output->height_pxl = height; } /** @@ -1227,13 +1333,17 @@ static void global_add(void *data, struct display_t *display = static_cast<struct display_t *>(data); if (!strcmp(interface, wl_compositor_interface.name)) { display->compositor = static_cast<wl_compositor *>( - wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1)); + wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3)); } else if (!strcmp(interface, xdg_wm_base_interface.name)) { display->xdg_shell = static_cast<xdg_wm_base *>( wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1)); xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr); } + else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)) { + display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>( + wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1)); + } else if (!strcmp(interface, wl_output_interface.name)) { output_t *output = new output_t; output->scale = 1; @@ -1335,16 +1445,6 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) wl_data_device_add_listener(input->data_device, &data_device_listener, input); } } - - const char *theme = std::getenv("XCURSOR_THEME"); - const char *size = std::getenv("XCURSOR_SIZE"); - const int sizei = size ? std::stoi(size) : default_cursor_size; - - d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm); - if (!d->cursor_theme) { - display_destroy(d); - throw std::runtime_error("Wayland: unable to access cursor themes!"); - } } GHOST_SystemWayland::~GHOST_SystemWayland() @@ -1471,8 +1571,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TU { if (getNumDisplays() > 0) { /* We assume first output as main. */ - width = uint32_t(d->outputs[0]->width); - height = uint32_t(d->outputs[0]->height); + width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale; + height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale; } } @@ -1481,7 +1581,7 @@ void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUn getMainDisplayDimensions(width, height); } -GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings) +GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/) { /* Create new off-screen window. */ wl_surface *os_surface = wl_compositor_create_surface(compositor()); @@ -1530,6 +1630,11 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, const bool is_dialog, const GHOST_IWindow *parentWindow) { + /* globally store pointer to window manager */ + if (!window_manager) { + window_manager = getWindowManager(); + } + GHOST_WindowWayland *window = new GHOST_WindowWayland( this, title, @@ -1574,6 +1679,21 @@ xdg_wm_base *GHOST_SystemWayland::shell() return d->xdg_shell; } +zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager() +{ + return d->xdg_decoration_manager; +} + +const std::vector<output_t *> &GHOST_SystemWayland::outputs() const +{ + return d->outputs; +} + +wl_shm *GHOST_SystemWayland::shm() const +{ + return d->shm; +} + void GHOST_SystemWayland::setSelection(const std::string &selection) { this->selection = selection; @@ -1581,23 +1701,20 @@ void GHOST_SystemWayland::setSelection(const std::string &selection) static void set_cursor_buffer(input_t *input, wl_buffer *buffer) { - input->cursor.visible = (buffer != nullptr); + cursor_t *c = &input->cursor; - wl_surface_attach(input->cursor.surface, buffer, 0, 0); - wl_surface_commit(input->cursor.surface); + c->visible = (buffer != nullptr); - if (input->cursor.visible) { - wl_surface_damage(input->cursor.surface, - 0, - 0, - int32_t(input->cursor.image.width), - int32_t(input->cursor.image.height)); - wl_pointer_set_cursor(input->pointer, - input->pointer_serial, - input->cursor.surface, - int32_t(input->cursor.image.hotspot_x), - int32_t(input->cursor.image.hotspot_y)); - } + wl_surface_attach(c->surface, buffer, 0, 0); + + wl_surface_damage(c->surface, 0, 0, int32_t(c->image.width), int32_t(c->image.height)); + wl_pointer_set_cursor(input->pointer, + input->pointer_serial, + c->visible ? c->surface : nullptr, + int32_t(c->image.hotspot_x) / c->scale, + int32_t(c->image.hotspot_y) / c->scale); + + wl_surface_commit(c->surface); } GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) @@ -1608,7 +1725,15 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) : cursors.at(GHOST_kStandardCursorDefault); - wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str()); + input_t *input = d->inputs[0]; + cursor_t *c = &input->cursor; + + if (!c->theme) { + /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */ + c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); + } + + wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str()); if (!cursor) { GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); @@ -1620,11 +1745,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) if (!buffer) { return GHOST_kFailure; } - cursor_t *c = &d->inputs[0]->cursor; + c->buffer = buffer; c->image = *image; - set_cursor_buffer(d->inputs[0], buffer); + set_cursor_buffer(input, buffer); return GHOST_kSuccess; } @@ -1735,6 +1860,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, wl_surface *surface) { + /* ignore, if the required protocols are not supported */ + if (!d->relative_pointer_manager || !d->pointer_constraints) { + return GHOST_kFailure; + } + if (d->inputs.empty()) { return GHOST_kFailure; } @@ -1754,6 +1884,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo break; case GHOST_kGrabNormal: + break; case GHOST_kGrabWrap: input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( d->relative_pointer_manager, input->pointer); diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 10b9ef6bd62..3a08a0d3b16 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -26,6 +26,7 @@ #include "GHOST_WindowWayland.h" #include <wayland-client.h> +#include <xdg-decoration-client-protocol.h> #include <xdg-shell-client-protocol.h> #include <string> @@ -34,6 +35,16 @@ class GHOST_WindowWayland; struct display_t; +struct output_t { + struct wl_output *output; + int32_t width_pxl, height_pxl; // dimensions in pixel + int32_t width_mm, height_mm; // dimensions in millimeter + int transform; + int scale; + std::string make; + std::string model; +}; + class GHOST_SystemWayland : public GHOST_System { public: GHOST_SystemWayland(); @@ -84,6 +95,12 @@ class GHOST_SystemWayland : public GHOST_System { xdg_wm_base *shell(); + zxdg_decoration_manager_v1 *decoration_manager(); + + const std::vector<output_t *> &outputs() const; + + wl_shm *shm() const; + void setSelection(const std::string &selection); GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 38dea9c1142..fc3cb81e26a 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -32,6 +32,7 @@ #include <commctrl.h> #include <psapi.h> #include <shellapi.h> +#include <shellscalingapi.h> #include <shlobj.h> #include <tlhelp32.h> #include <windowsx.h> @@ -97,41 +98,6 @@ # define VK_GR_LESS 0xE2 #endif // VK_GR_LESS -#ifndef VK_MEDIA_NEXT_TRACK -# define VK_MEDIA_NEXT_TRACK 0xB0 -#endif // VK_MEDIA_NEXT_TRACK -#ifndef VK_MEDIA_PREV_TRACK -# define VK_MEDIA_PREV_TRACK 0xB1 -#endif // VK_MEDIA_PREV_TRACK -#ifndef VK_MEDIA_STOP -# define VK_MEDIA_STOP 0xB2 -#endif // VK_MEDIA_STOP -#ifndef VK_MEDIA_PLAY_PAUSE -# define VK_MEDIA_PLAY_PAUSE 0xB3 -#endif // VK_MEDIA_PLAY_PAUSE - -// Window message newer than Windows 7 -#ifndef WM_DPICHANGED -# define WM_DPICHANGED 0x02E0 -#endif // WM_DPICHANGED - -// WM_POINTER API messages minimum Windows 7 -#ifndef WM_POINTERENTER -# define WM_POINTERENTER 0x0249 -#endif // WM_POINTERENTER -#ifndef WM_POINTERDOWN -# define WM_POINTERDOWN 0x0246 -#endif // WM_POINTERDOWN -#ifndef WM_POINTERUPDATE -# define WM_POINTERUPDATE 0x0245 -#endif // WM_POINTERUPDATE -#ifndef WM_POINTERUP -# define WM_POINTERUP 0x0247 -#endif // WM_POINTERUP -#ifndef WM_POINTERLEAVE -# define WM_POINTERLEAVE 0x024A -#endif // WM_POINTERLEAVE - /* Workaround for some laptop touchpads, some of which seems to * have driver issues which makes it so window function receives * the message, but PeekMessage doesn't pick those messages for @@ -173,24 +139,6 @@ static void initRawInput() #undef DEVICE_COUNT } -#ifndef DPI_ENUMS_DECLARED -typedef enum PROCESS_DPI_AWARENESS { - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 -} PROCESS_DPI_AWARENESS; - -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; - -# define USER_DEFAULT_SCREEN_DPI 96 - -# define DPI_ENUMS_DECLARED -#endif typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); @@ -205,15 +153,7 @@ GHOST_SystemWin32::GHOST_SystemWin32() // Tell Windows we are per monitor DPI aware. This disables the default // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. - HMODULE m_shcore = ::LoadLibrary("Shcore.dll"); - if (m_shcore) { - GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness = - (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness"); - - if (fpSetProcessDpiAwareness) { - fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } - } + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); // Check if current keyboard layout uses AltGr and save keylayout ID for // specialized handling if keys like VK_OEM_*. I.e. french keylayout @@ -581,14 +521,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() InitCommonControls(); /* Disable scaling on high DPI displays on Vista */ - HMODULE - user32 = ::LoadLibraryA("user32.dll"); - typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)(); - LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress( - user32, "SetProcessDPIAware"); - if (SetProcessDPIAware) - SetProcessDPIAware(); - FreeLibrary(user32); + SetProcessDPIAware(); initRawInput(); m_lfstart = ::GetTickCount(); diff --git a/intern/ghost/intern/GHOST_WaylandCursorSettings.h b/intern/ghost/intern/GHOST_WaylandCursorSettings.h new file mode 100644 index 00000000000..c7d2d82ff84 --- /dev/null +++ b/intern/ghost/intern/GHOST_WaylandCursorSettings.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#pragma once +#include <dbus/dbus.h> +#include <string> + +static DBusMessage *get_setting_sync(DBusConnection *const connection, + const char *key, + const char *value) +{ + DBusError error; + dbus_bool_t success; + DBusMessage *message; + DBusMessage *reply; + + dbus_error_init(&error); + + message = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read"); + + success = dbus_message_append_args( + message, DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID); + + if (!success) { + return NULL; + } + + reply = dbus_connection_send_with_reply_and_block( + connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error); + + dbus_message_unref(message); + + if (dbus_error_is_set(&error)) { + return NULL; + } + + return reply; +} + +static bool parse_type(DBusMessage *const reply, const int type, void *value) +{ + DBusMessageIter iter[3]; + + dbus_message_iter_init(reply, &iter[0]); + if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) { + return false; + } + + dbus_message_iter_recurse(&iter[0], &iter[1]); + if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) { + return false; + } + + dbus_message_iter_recurse(&iter[1], &iter[2]); + if (dbus_message_iter_get_arg_type(&iter[2]) != type) { + return false; + } + + dbus_message_iter_get_basic(&iter[2], value); + + return true; +} + +static bool get_cursor_settings(std::string &theme, int &size) +{ + static const char name[] = "org.gnome.desktop.interface"; + static const char key_theme[] = "cursor-theme"; + static const char key_size[] = "cursor-size"; + + DBusError error; + DBusConnection *connection; + DBusMessage *reply; + const char *value_theme = NULL; + + dbus_error_init(&error); + + connection = dbus_bus_get(DBUS_BUS_SESSION, &error); + + if (dbus_error_is_set(&error)) { + return false; + } + + reply = get_setting_sync(connection, name, key_theme); + if (!reply) { + return false; + } + + if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) { + dbus_message_unref(reply); + return false; + } + + theme = std::string(value_theme); + + dbus_message_unref(reply); + + reply = get_setting_sync(connection, name, key_size); + if (!reply) { + return false; + } + + if (!parse_type(reply, DBUS_TYPE_INT32, &size)) { + dbus_message_unref(reply); + return false; + } + + dbus_message_unref(reply); + + return true; +} diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 1e1d0814d3a..cbac2d6eaa1 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -29,11 +29,19 @@ #include <wayland-egl.h> +static constexpr size_t base_dpi = 96; + struct window_t { GHOST_WindowWayland *w; wl_surface *surface; + // outputs on which the window is currently shown on + std::unordered_set<const output_t *> outputs; + GHOST_TUns16 dpi = 0; + int scale = 1; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; + struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr; + enum zxdg_toplevel_decoration_v1_mode decoration_mode; wl_egl_window *egl_window; int32_t pending_width, pending_height; bool is_maximised; @@ -93,17 +101,30 @@ static const xdg_toplevel_listener toplevel_listener = { toplevel_close, }; +static void toplevel_decoration_configure( + void *data, + struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/, + uint32_t mode) +{ + static_cast<window_t *>(data)->decoration_mode = zxdg_toplevel_decoration_v1_mode(mode); +} + +static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = { + toplevel_decoration_configure, +}; + static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) { window_t *win = static_cast<window_t *>(data); - int w, h; - wl_egl_window_get_attached_size(win->egl_window, &w, &h); - if (win->pending_width != 0 && win->pending_height != 0 && win->pending_width != w && - win->pending_height != h) { - win->width = win->pending_width; - win->height = win->pending_height; - wl_egl_window_resize(win->egl_window, win->pending_width, win->pending_height, 0, 0); + if (win->xdg_surface != xdg_surface) { + return; + } + + if (win->pending_width != 0 && win->pending_height != 0) { + win->width = win->scale * win->pending_width; + win->height = win->scale * win->pending_height; + wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0); win->pending_width = 0; win->pending_height = 0; win->w->notify_size(); @@ -123,6 +144,52 @@ static const xdg_surface_listener surface_listener = { surface_configure, }; +static bool update_scale(GHOST_WindowWayland *window) +{ + int scale = 0; + for (const output_t *output : window->outputs_active()) { + if (output->scale > scale) + scale = output->scale; + } + + if (scale > 0 && window->scale() != scale) { + window->scale() = scale; + // using the real DPI will cause wrong scaling of the UI + // use a multiplier for the default DPI as workaround + window->dpi() = scale * base_dpi; + wl_surface_set_buffer_scale(window->surface(), scale); + return true; + } + return false; +} + +static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +{ + GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); + for (const output_t *reg_output : w->outputs()) { + if (reg_output->output == output) { + w->outputs_active().insert(reg_output); + } + } + update_scale(w); +} + +static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +{ + GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data); + for (const output_t *reg_output : w->outputs()) { + if (reg_output->output == output) { + w->outputs_active().erase(reg_output); + } + } + update_scale(w); +} + +struct wl_surface_listener wl_surface_listener = { + surface_enter, + surface_leave, +}; + /** \} */ /* -------------------------------------------------------------------- */ @@ -161,17 +228,28 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, /* Window surfaces. */ w->surface = wl_compositor_create_surface(m_system->compositor()); + wl_surface_add_listener(w->surface, &wl_surface_listener, this); + w->egl_window = wl_egl_window_create(w->surface, int(width), int(height)); w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); + if (m_system->decoration_manager()) { + w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + m_system->decoration_manager(), w->xdg_toplevel); + zxdg_toplevel_decoration_v1_add_listener( + w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w); + zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + wl_surface_set_user_data(w->surface, this); xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); - if (parentWindow) { + if (parentWindow && is_dialog) { xdg_toplevel_set_parent( w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel); } @@ -192,6 +270,9 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, if (setDrawingContextType(type) == GHOST_kFailure) { GHOST_PRINT("Failed to create EGL context" << std::endl); } + + /* set swap interval to 0 to prevent blocking */ + setSwapInterval(0); } GHOST_TSuccess GHOST_WindowWayland::close() @@ -226,6 +307,31 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size() new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); } +wl_surface *GHOST_WindowWayland::surface() const +{ + return w->surface; +} + +const std::vector<output_t *> &GHOST_WindowWayland::outputs() const +{ + return m_system->outputs(); +} + +std::unordered_set<const output_t *> &GHOST_WindowWayland::outputs_active() +{ + return w->outputs; +} + +uint16_t &GHOST_WindowWayland::dpi() +{ + return w->dpi; +} + +int &GHOST_WindowWayland::scale() +{ + return w->scale; +} + GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { return m_system->setCursorGrab(mode, w->surface); @@ -310,6 +416,9 @@ GHOST_WindowWayland::~GHOST_WindowWayland() releaseNativeHandles(); wl_egl_window_destroy(w->egl_window); + if (w->xdg_toplevel_decoration) { + zxdg_toplevel_decoration_v1_destroy(w->xdg_toplevel_decoration); + } xdg_toplevel_destroy(w->xdg_toplevel); xdg_surface_destroy(w->xdg_surface); wl_surface_destroy(w->surface); @@ -317,6 +426,11 @@ GHOST_WindowWayland::~GHOST_WindowWayland() delete w; } +GHOST_TUns16 GHOST_WindowWayland::getDPIHint() +{ + return w->dpi; +} + GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible) { return m_system->setCursorVisibility(visible); diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h index b62b5c24d60..dbddc7c469e 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.h +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -24,9 +24,14 @@ #include "GHOST_Window.h" +#include <unordered_set> +#include <vector> + class GHOST_SystemWayland; struct window_t; +struct wl_surface; +struct output_t; class GHOST_WindowWayland : public GHOST_Window { public: @@ -47,6 +52,8 @@ class GHOST_WindowWayland : public GHOST_Window { ~GHOST_WindowWayland() override; + GHOST_TUns16 getDPIHint() override; + GHOST_TSuccess close(); GHOST_TSuccess activate(); @@ -55,6 +62,16 @@ class GHOST_WindowWayland : public GHOST_Window { GHOST_TSuccess notify_size(); + wl_surface *surface() const; + + const std::vector<output_t *> &outputs() const; + + std::unordered_set<const output_t *> &outputs_active(); + + uint16_t &dpi(); + + int &scale(); + protected: GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override; diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index bf142239606..eeafe333633 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -84,9 +84,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_wantAlphaBackground(alphaBackground), m_normal_state(GHOST_kWindowStateNormal), m_user32(NULL), - m_fpGetPointerInfoHistory(NULL), - m_fpGetPointerPenInfoHistory(NULL), - m_fpGetPointerTouchInfoHistory(NULL), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), m_debug_context(is_debug) { @@ -121,19 +118,21 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, monitor.dwFlags = 0; GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor); - /* Constrain size to fit within this monitor. */ + /* Constrain requested size and position to fit within this monitor. */ width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left); height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top); - win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width); win_rect.right = win_rect.left + width; win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height); win_rect.bottom = win_rect.top + height; - /* Adjust our requested values to allow for caption, borders, shadows, etc. - Windows API Note: You cannot specify WS_OVERLAPPED when calling. */ + /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be + * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */ AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style); + /* But never allow a top position that can hide part of the title bar. */ + win_rect.top = max(monitor.rcWork.top, win_rect.top); + m_hWnd = ::CreateWindowExW(extended_style, // window extended style s_windowClassName, // pointer to registered class name title_16, // pointer to window name @@ -151,19 +150,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_user32 = ::LoadLibrary("user32.dll"); if (m_hWnd) { - if (m_user32) { - // Touch enabled screens with pen support by default have gestures - // enabled, which results in a delay between the pointer down event - // and the first move when using the stylus. RegisterTouchWindow - // disables the new gesture architecture enabling the events to be - // sent immediately to the application rather than being absorbed by - // the gesture API. - GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow) - GetProcAddress(m_user32, "RegisterTouchWindow"); - if (pRegisterTouchWindow) { - pRegisterTouchWindow(m_hWnd, 0); - } - } + RegisterTouchWindow(m_hWnd, 0); // Register this window as a droptarget. Requires m_hWnd to be valid. // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32. @@ -230,16 +217,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, } } - // Initialize Windows Ink - if (m_user32) { - m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress( - m_user32, "GetPointerInfoHistory"); - m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress( - m_user32, "GetPointerPenInfoHistory"); - m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress( - m_user32, "GetPointerTouchInfoHistory"); - } - // Initialize Wintab m_wintab.handle = ::LoadLibrary("Wintab32.dll"); if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) { @@ -324,9 +301,6 @@ GHOST_WindowWin32::~GHOST_WindowWin32() if (m_user32) { FreeLibrary(m_user32); m_user32 = NULL; - m_fpGetPointerInfoHistory = NULL; - m_fpGetPointerPenInfoHistory = NULL; - m_fpGetPointerTouchInfoHistory = NULL; } if (m_customCursor) { @@ -948,15 +922,14 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); GHOST_TUns32 outCount; - if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) { + if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { return GHOST_kFailure; } auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount); outPointerInfo.resize(outCount); - if (!(m_fpGetPointerPenInfoHistory && - m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { + if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index b004f7e7b19..a13bd876667 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -53,177 +53,12 @@ typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID); typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL); -// typedef to user32 functions to disable gestures on windows -typedef BOOL(API *GHOST_WIN32_RegisterTouchWindow)(HWND hwnd, ULONG ulFlags); - // typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); #ifndef USER_DEFAULT_SCREEN_DPI # define USER_DEFAULT_SCREEN_DPI 96 #endif // USER_DEFAULT_SCREEN_DPI -// typedefs for user32 functions to allow pointer functions -enum tagPOINTER_INPUT_TYPE { - PT_POINTER = 1, // Generic pointer - PT_TOUCH = 2, // Touch - PT_PEN = 3, // Pen - PT_MOUSE = 4, // Mouse -#if (WINVER >= 0x0603) - PT_TOUCHPAD = 5, // Touchpad -#endif /* WINVER >= 0x0603 */ -}; - -typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { - POINTER_CHANGE_NONE, - POINTER_CHANGE_FIRSTBUTTON_DOWN, - POINTER_CHANGE_FIRSTBUTTON_UP, - POINTER_CHANGE_SECONDBUTTON_DOWN, - POINTER_CHANGE_SECONDBUTTON_UP, - POINTER_CHANGE_THIRDBUTTON_DOWN, - POINTER_CHANGE_THIRDBUTTON_UP, - POINTER_CHANGE_FOURTHBUTTON_DOWN, - POINTER_CHANGE_FOURTHBUTTON_UP, - POINTER_CHANGE_FIFTHBUTTON_DOWN, - POINTER_CHANGE_FIFTHBUTTON_UP, -} POINTER_BUTTON_CHANGE_TYPE; - -typedef DWORD POINTER_INPUT_TYPE; -typedef UINT32 POINTER_FLAGS; - -#define POINTER_FLAG_NONE 0x00000000 -#define POINTER_FLAG_NEW 0x00000001 -#define POINTER_FLAG_INRANGE 0x00000002 -#define POINTER_FLAG_INCONTACT 0x00000004 -#define POINTER_FLAG_FIRSTBUTTON 0x00000010 -#define POINTER_FLAG_SECONDBUTTON 0x00000020 -#define POINTER_FLAG_THIRDBUTTON 0x00000040 -#define POINTER_FLAG_FOURTHBUTTON 0x00000080 -#define POINTER_FLAG_FIFTHBUTTON 0x00000100 -#define POINTER_FLAG_PRIMARY 0x00002000 -#define POINTER_FLAG_CONFIDENCE 0x000004000 -#define POINTER_FLAG_CANCELED 0x000008000 -#define POINTER_FLAG_DOWN 0x00010000 -#define POINTER_FLAG_UPDATE 0x00020000 -#define POINTER_FLAG_UP 0x00040000 -#define POINTER_FLAG_WHEEL 0x00080000 -#define POINTER_FLAG_HWHEEL 0x00100000 -#define POINTER_FLAG_CAPTURECHANGED 0x00200000 -#define POINTER_FLAG_HASTRANSFORM 0x00400000 - -typedef struct tagPOINTER_INFO { - POINTER_INPUT_TYPE pointerType; - UINT32 pointerId; - UINT32 frameId; - POINTER_FLAGS pointerFlags; - HANDLE sourceDevice; - HWND hwndTarget; - POINT ptPixelLocation; - POINT ptHimetricLocation; - POINT ptPixelLocationRaw; - POINT ptHimetricLocationRaw; - DWORD dwTime; - UINT32 historyCount; - INT32 InputData; - DWORD dwKeyStates; - UINT64 PerformanceCount; - POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; -} POINTER_INFO; - -typedef UINT32 PEN_FLAGS; -#define PEN_FLAG_NONE 0x00000000 // Default -#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed -#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted -#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed - -typedef UINT32 PEN_MASK; -#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid -#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid -#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid -#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid - -typedef struct tagPOINTER_PEN_INFO { - POINTER_INFO pointerInfo; - PEN_FLAGS penFlags; - PEN_MASK penMask; - UINT32 pressure; - UINT32 rotation; - INT32 tiltX; - INT32 tiltY; -} POINTER_PEN_INFO; - -/* - * Flags that appear in pointer input message parameters - */ -#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer -#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed -#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact -#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action -#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action -#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button -#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button -#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button -#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary -#define POINTER_MESSAGE_FLAG_CONFIDENCE \ - 0x00004000 // Pointer is considered unlikely to be accidental -#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner - -typedef UINT32 TOUCH_FLAGS; -#define TOUCH_FLAG_NONE 0x00000000 // Default - -typedef UINT32 TOUCH_MASK; -#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid -#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid -#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid - -typedef struct tagPOINTER_TOUCH_INFO { - POINTER_INFO pointerInfo; - TOUCH_FLAGS touchFlags; - TOUCH_MASK touchMask; - RECT rcContact; - RECT rcContactRaw; - UINT32 orientation; - UINT32 pressure; -} POINTER_TOUCH_INFO; - -/* - * Macros to retrieve information from pointer input message parameters - */ -#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) -#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag)) -#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW) -#define IS_POINTER_INRANGE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE) -#define IS_POINTER_INCONTACT_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT) -#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON) -#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON) -#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON) -#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON) -#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON) -#define IS_POINTER_PRIMARY_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY) -#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE) -#define IS_POINTER_CANCELED_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) - -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_INFO *pointerInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_PEN_INFO *penInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_TOUCH_INFO *touchInfo); - struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; GHOST_TInt32 isPrimary; @@ -576,9 +411,6 @@ class GHOST_WindowWin32 : public GHOST_Window { /** `user32.dll` handle */ HMODULE m_user32; - GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory; - GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory; - GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory; HWND m_parentWindowHwnd; diff --git a/intern/mikktspace/README.md b/intern/mikktspace/README.md new file mode 100644 index 00000000000..9fda1559e44 --- /dev/null +++ b/intern/mikktspace/README.md @@ -0,0 +1,4 @@ +# MikkTSpace +A common standard for tangent space used in baking tools to produce normal maps. + +More information can be found at http://www.mikktspace.com/. diff --git a/release/darwin/README.md b/release/darwin/README.md new file mode 100644 index 00000000000..f1f02543ff3 --- /dev/null +++ b/release/darwin/README.md @@ -0,0 +1,5 @@ +Buildbot Configuration +====================== + +Files used by Buildbot's `package-code-binaires` step for the darwin platform. + diff --git a/release/darwin/README.txt b/release/darwin/README.txt deleted file mode 100644 index 626ce8820ab..00000000000 --- a/release/darwin/README.txt +++ /dev/null @@ -1,55 +0,0 @@ - -macOS app bundling guide -======================== - -Install Code Signing Certificate --------------------------------- - -* Go to https://developer.apple.com/account/resources/certificates/list -* Download the Developer ID Application certificate. -* Double click the file and add to key chain (default options). -* Delete the file from the Downloads folder. - -* You will also need to install a .p12 public/private key file for the - certificate. This is only available for the owner of the Blender account, - or can be exported and copied from another system that already has code - signing set up. - -Find the codesigning identity by running: - -$ security find-identity -v -p codesigning - -"Developer ID Application: Stichting Blender Foundation" is the identity needed. -The long code at the start of the line is used as <identity> below. - -Setup Apple ID --------------- - -* The Apple ID must have two step verification enabled. -* Create an app specific password for the code signing app (label can be anything): -https://support.apple.com/en-us/HT204397 -* Add the app specific password to keychain: - -$ security add-generic-password -a <apple-id> -w <app-specific-password> -s altool-password - -When running the bundle script, there will be a popup. To avoid that either: -* Click Always Allow in the popup -* In the Keychain Access app, change the Access Control settings on altool-password - -Bundle ------- - -Then the bundle is created as follows: - -$ ./bundle.sh --source <sourcedir> --dmg <dmg> --bundle-id <bundleid> --username <apple-id> --password "@keychain:altool-password" --codesign <identity> - -<sourcedir> directory where built Blender.app is -<dmg> location and name of the final disk image -<bundleid> id on notarization, for example org.blenderfoundation.blender.release -<apple-id> your appleid email -<identity> codesigning identity - -When specifying only --sourcedir and --dmg, the build will not be signed. - -Example : -$ ./bundle.sh --source /data/build/bin --dmg /data/Blender-2.8-alpha-macOS-10.11.dmg --bundle-id org.blenderfoundation.blender.release --username "foo@mac.com" --password "@keychain:altool-password" --codesign AE825E26F12D08B692F360133210AF46F4CF7B97 diff --git a/release/darwin/blender.applescript b/release/darwin/blender.applescript deleted file mode 100644 index 130dc2eb64c..00000000000 --- a/release/darwin/blender.applescript +++ /dev/null @@ -1,18 +0,0 @@ -tell application "Finder" - tell disk "Blender" - open - set current view of container window to icon view - set toolbar visible of container window to false - set statusbar visible of container window to false - set the bounds of container window to {100, 100, 640, 472} - set theViewOptions to icon view options of container window - set arrangement of theViewOptions to not arranged - set icon size of theViewOptions to 128 - set background picture of theViewOptions to file ".background:background.tif" - set position of item " " of container window to {400, 190} - set position of item "blender.app" of container window to {135, 190} - update without registering applications - delay 5 - close - end tell -end tell diff --git a/release/darwin/bundle.sh b/release/darwin/bundle.sh deleted file mode 100755 index 6d8695a441d..00000000000 --- a/release/darwin/bundle.sh +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env bash -# -# Script to create a macOS dmg file for Blender builds, including code -# signing and notarization for releases. - -# Check that we have all needed tools. -for i in osascript git codesign hdiutil xcrun ; do - if [ ! -x "$(which ${i})" ]; then - echo "Unable to execute command $i, macOS broken?" - exit 1 - fi -done - -# Defaults settings. -_script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -_volume_name="Blender" -_tmp_dir="$(mktemp -d)" -_tmp_dmg="/tmp/blender-tmp.dmg" -_background_image="${_script_dir}/background.tif" -_mount_dir="/Volumes/${_volume_name}" -_entitlements="${_script_dir}/entitlements.plist" - -# Handle arguments. -while [[ $# -gt 0 ]]; do - key=$1 - case $key in - -s|--source) - SRC_DIR="$2" - shift - shift - ;; - -d|--dmg) - DEST_DMG="$2" - shift - shift - ;; - -b|--bundle-id) - N_BUNDLE_ID="$2" - shift - shift - ;; - -u|--username) - N_USERNAME="$2" - shift - shift - ;; - -p|--password) - N_PASSWORD="$2" - shift - shift - ;; - -c|--codesign) - C_CERT="$2" - shift - shift - ;; - --background-image) - _background_image="$2" - shift - shift - ;; - -h|--help) - echo "Usage:" - echo " $(basename "$0") --source DIR --dmg IMAGENAME " - echo " optional arguments:" - echo " --codesign <certname>" - echo " --username <username>" - echo " --password <password>" - echo " --bundle-id <bundleid>" - echo " Check https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow " - exit 1 - ;; - esac -done - -if [ ! -d "${SRC_DIR}/Blender.app" ]; then - echo "use --source parameter to set source directory where Blender.app can be found" - exit 1 -fi - -if [ -z "${DEST_DMG}" ]; then - echo "use --dmg parameter to set output dmg name" - exit 1 -fi - -# Destroy destination dmg if there is any. -test -f "${DEST_DMG}" && rm "${DEST_DMG}" -if [ -d "${_mount_dir}" ]; then - echo -n "Ejecting existing blender volume.." - DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }') - diskutil eject "${DEV_FILE}" || exit 1 - echo -fi - -# Copy dmg contents. -echo -n "Copying Blender.app..." -cp -r "${SRC_DIR}/Blender.app" "${_tmp_dir}/" || exit 1 -echo - -# Create the disk image. -_directory_size=$(du -sh ${_tmp_dir} | awk -F'[^0-9]*' '$0=$1') -_image_size=$(echo "${_directory_size}" + 400 | bc) # extra 400 need for codesign to work (why on earth?) - -echo -echo -n "Creating disk image of size ${_image_size}M.." -test -f "${_tmp_dmg}" && rm "${_tmp_dmg}" -hdiutil create -size "${_image_size}m" -fs HFS+ -srcfolder "${_tmp_dir}" -volname "${_volume_name}" -format UDRW "${_tmp_dmg}" -mode 755 - -echo "Mounting readwrite image..." -hdiutil attach -readwrite -noverify -noautoopen "${_tmp_dmg}" - -echo "Setting background picture.." -if ! test -z "${_background_image}"; then - echo "Copying background image ..." - test -d "${_mount_dir}/.background" || mkdir "${_mount_dir}/.background" - _background_image_NAME=$(basename "${_background_image}") - cp "${_background_image}" "${_mount_dir}/.background/${_background_image_NAME}" -fi - -echo "Creating link to /Applications ..." -ln -s /Applications "${_mount_dir}/Applications" -echo "Renaming Applications to empty string." -mv ${_mount_dir}/Applications "${_mount_dir}/ " - -echo "Running applescript to set folder looks ..." -cat "${_script_dir}/blender.applescript" | osascript - -echo "Waiting after applescript ..." -sleep 5 - -if [ ! -z "${C_CERT}" ]; then - # Codesigning requires all libs and binaries to be signed separately. - echo -n "Codesigning Python" - for f in $(find "${_mount_dir}/Blender.app/Contents/Resources" -name "python*"); do - if [ -x ${f} ] && [ ! -d ${f} ]; then - codesign --remove-signature "${f}" - codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}" - fi - done - echo ; echo -n "Codesigning .dylib and .so libraries" - for f in $(find "${_mount_dir}/Blender.app" -name "*.dylib" -o -name "*.so"); do - codesign --remove-signature "${f}" - codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}" - done - echo ; echo -n "Codesigning Blender.app" - codesign --remove-signature "${_mount_dir}/Blender.app" - codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${_mount_dir}/Blender.app" - echo -else - echo "No codesigning cert given, skipping..." -fi - -# Need to eject dev files to remove /dev files and free .dmg for converting -echo "Unmounting rw disk image ..." -DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }') -diskutil eject "${DEV_FILE}" - -sleep 3 - -echo "Compressing disk image ..." -hdiutil convert "${_tmp_dmg}" -format UDZO -o "${DEST_DMG}" - -# Codesign the dmg -if [ ! -z "${C_CERT}" ]; then - echo -n "Codesigning dmg..." - codesign --timestamp --force --sign "${C_CERT}" "${DEST_DMG}" - echo -fi - -# Cleanup -rm -rf "${_tmp_dir}" -rm "${_tmp_dmg}" - -# Notarize -if [ ! -z "${N_USERNAME}" ] && [ ! -z "${N_PASSWORD}" ] && [ ! -z "${N_BUNDLE_ID}" ]; then - # Send to Apple - echo "Sending ${DEST_DMG} for notarization..." - _tmpout=$(mktemp) - echo xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}" - xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1 - - # Parse request uuid - _requuid=$(cat "${_tmpout}" | grep "RequestUUID" | awk '{ print $3 }') - echo "RequestUUID: ${_requuid}" - if [ ! -z "${_requuid}" ]; then - # Wait for Apple to confirm notarization is complete - echo "Waiting for notarization to be complete.." - for c in {20..0};do - sleep 600 - xcrun altool --notarization-info "${_requuid}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1 - _status=$(cat "${_tmpout}" | grep "Status:" | awk '{ print $2 }') - if [ "${_status}" == "invalid" ]; then - echo "Got invalid notarization!" - break; - fi - - if [ "${_status}" == "success" ]; then - echo -n "Notarization successful! Stapling..." - xcrun stapler staple -v "${DEST_DMG}" - break; - fi - echo "Notarization in progress, waiting..." - done - else - cat ${_tmpout} - echo "Error getting RequestUUID, notarization unsuccessful" - fi -else - echo "No notarization credentials supplied, skipping..." -fi - -echo "..done. You should have ${DEST_DMG} ready to upload" diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 5ab29b1331d2103dae634b987f121c4599459d7 +Subproject 2cef4877edc40875978c4e95322bb5193f5815b diff --git a/release/freedesktop/org.blender.Blender.appdata.xml b/release/freedesktop/org.blender.Blender.appdata.xml index f6d17834150..7a5a252e4ca 100644 --- a/release/freedesktop/org.blender.Blender.appdata.xml +++ b/release/freedesktop/org.blender.Blender.appdata.xml @@ -40,6 +40,25 @@ </screenshot> </screenshots> <releases> + <release version="2.93" date="2021-06-02"> + <description> + <p>New features:</p> + <ul> + <li>Mesh primitive nodes</li> + <li>Line Art</li> + <li>EEVEE Realistic depth of field and volumetrics</li> + <li>Spreadsheet editor</li> + </ul> + <p>Enhancements:</p> + <ul> + <li>Geometry nodes 22 new nodes and imrpoved attribute search</li> + <li>Mask loops, textures and patterns for sculpting</li> + <li>Grease pencil interpolate refactored and SVG and PDF support</li> + <li>Persistent Data rendering settings for Cycles</li> + <li>Video Sequencer Editor auto-proxy system</li> + </ul> + </description> + </release> <release version="2.92" date="2021-02-25"> <description> <p>New features:</p> diff --git a/release/freedesktop/snap/README.md b/release/freedesktop/snap/README.md new file mode 100644 index 00000000000..742b265ada6 --- /dev/null +++ b/release/freedesktop/snap/README.md @@ -0,0 +1,17 @@ +Snap Configuration +=================== + +Files used by Buildbot's `package-code-store-snap` and `deliver-code-store-snap` steps. + +Build pipeline snap tracks and channels + +``` + <track>/stable + - Latest stable release for the specified track + <track>/candidate + - Test builds for the upcoming stable release - *not used for now* + <track>/beta + - Nightly automated builds provided by a release branch + <track>/egde/<branch> + - Nightly or on demand builds - will also make use of branch +``` diff --git a/release/freedesktop/snap/README.txt b/release/freedesktop/snap/README.txt deleted file mode 100644 index 2e8822f32dc..00000000000 --- a/release/freedesktop/snap/README.txt +++ /dev/null @@ -1,38 +0,0 @@ - -Snap Package Instructions -========================= - -This folder contains the scripts for creating and uploading the snap on: -https://snapcraft.io/blender - - -Setup ------ - -This has only been tested to work on Ubuntu. - -# Install required packages -sudo apt install snapd snapcraft - - -Steps ------ - -# Build the snap file -python3 bundle.py --version 2.XX --url https://download.blender.org/release/Blender2.XX/blender-2.XX-x86_64.tar.bz2 - -# Install snap to test -# --dangerous is needed since the snap has not been signed yet -# --classic is required for installing Blender in general -sudo snap install --dangerous --classic blender_2.XX_amd64.snap - -# Upload -snapcraft push --release=stable blender_2.XX_amd64.snap - - -Release Values --------------- - -stable: final release -candidate: release candidates - diff --git a/release/freedesktop/snap/snapcraft.yaml.in b/release/freedesktop/snap/blender-snapcraft-template.yaml index eb3ef97eba8..882f9081c09 100644 --- a/release/freedesktop/snap/snapcraft.yaml.in +++ b/release/freedesktop/snap/blender-snapcraft-template.yaml @@ -10,12 +10,7 @@ description: | scientists, students, VFX experts, animators, game artists, modders, and the list goes on. - The standard snap channels are used in the following way: - - stable - Latest stable release. - candidate - Test builds for the upcoming stable release. - -icon: ../icons/scalable/apps/blender.svg +icon: @ICON_PATH@ passthrough: license: GPL-3.0 @@ -27,13 +22,14 @@ apps: command: ./blender-wrapper desktop: ./blender.desktop +base: core18 version: '@VERSION@' grade: @GRADE@ parts: blender: plugin: dump - source: @URL@ + source: @PACKAGE_PATH@ build-attributes: [keep-execstack, no-patchelf] override-build: | snapcraftctl build @@ -47,7 +43,7 @@ parts: - libxrender1 - libxxf86vm1 wrapper: - plugin: copy + plugin: dump source: . - files: - blender-wrapper: blender-wrapper + stage: + - ./blender-wrapper diff --git a/release/freedesktop/snap/bundle.py b/release/freedesktop/snap/bundle.py deleted file mode 100755 index c3ecc5af561..00000000000 --- a/release/freedesktop/snap/bundle.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import pathlib -import subprocess - -parser = argparse.ArgumentParser() -parser.add_argument("--version", required=True) -parser.add_argument("--url", required=True) -parser.add_argument("--grade", default="stable", choices=["stable", "devel"]) -args = parser.parse_args() - -yaml_text = pathlib.Path("snapcraft.yaml.in").read_text() -yaml_text = yaml_text.replace("@VERSION@", args.version) -yaml_text = yaml_text.replace("@URL@", args.url) -yaml_text = yaml_text.replace("@GRADE@", args.grade) -pathlib.Path("snapcraft.yaml").write_text(yaml_text) - -subprocess.call(["snapcraft", "clean"]) -subprocess.call(["snapcraft", "snap"]) diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd +Subproject 27fe7f3a4f964b53af436c4da4ddea337eff0c7 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 7d78c8a63f2f4b146f9327ddc0d567a5921b94e +Subproject 5a82baad9f986722104280e8354a4427d8e9eab diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py index 979b47f7a14..51ea53c0eba 100644 --- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -37,6 +37,9 @@ OBJECT_TYPES_RENDER = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'} def ids_nolib(bids): return (bid for bid in bids if not bid.library) +def ids_nolib_with_preview(bids): + return (bid for bid in bids if (not bid.library and bid.preview)) + def rna_backup_gen(data, include_props=None, exclude_props=None, root=()): # only writable properties... @@ -313,8 +316,9 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern): image = bpy.data.images[render_context.image, None] item = getattr(bpy.data, item_container)[item_name, None] image.reload() - item.preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE) - item.preview.image_pixels_float[:] = image.pixels + preview = item.preview_ensure() + preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE) + preview.image_pixels_float[:] = image.pixels # And now, main code! do_save = True @@ -451,15 +455,15 @@ def do_clear_previews(do_objects, do_collections, do_scenes, do_data_intern): bpy.ops.wm.previews_clear(id_type={'SHADING'}) if do_objects: - for ob in ids_nolib(bpy.data.objects): + for ob in ids_nolib_with_preview(bpy.data.objects): ob.preview.image_size = (0, 0) if do_collections: - for grp in ids_nolib(bpy.data.collections): + for grp in ids_nolib_with_preview(bpy.data.collections): grp.preview.image_size = (0, 0) if do_scenes: - for scene in ids_nolib(bpy.data.scenes): + for scene in ids_nolib_with_preview(bpy.data.scenes): scene.preview.image_size = (0, 0) print("Saving %s..." % bpy.data.filepath) diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index e9e9671cc35..1de4542e69e 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -198,6 +198,7 @@ def _clean_utf8(name): _display_name_literals = { ":": "_colon_", "+": "_plus_", + "/": "_slash_", } diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 7e2d09efcd4..552f7eeb4f3 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -58,6 +58,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), ("bpy.types.fluiddomainsettings.use_collision_border_bottom*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-bottom"), ("bpy.types.fluiddomainsettings.vector_scale_with_magnitude*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-scale-with-magnitude"), + ("bpy.types.spacespreadsheet.display_context_path_collapsed*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-display-context-path-collapsed"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), ("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"), @@ -66,16 +67,20 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.use_collision_border_left*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-left"), ("bpy.types.rendersettings_simplify_gpencil_view_modifier*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-modifier"), ("bpy.types.brushgpencilsettings.use_settings_stabilizer*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-stabilizer"), + ("bpy.types.colormanagedsequencercolorspacesettings.name*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings-name"), ("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"), ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), ("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.spaceoutliner.use_filter_lib_override_system*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override-system"), ("bpy.types.toolsettings.use_transform_pivot_point_align*", "scene_layout/object/tools/tool_settings.html#bpy-types-toolsettings-use-transform-pivot-point-align"), ("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.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"), ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), + ("bpy.types.colormanageddisplaysettings.display_device*", "render/color_management.html#bpy-types-colormanageddisplaysettings-display-device"), + ("bpy.types.colormanagedviewsettings.use_curve_mapping*", "render/color_management.html#bpy-types-colormanagedviewsettings-use-curve-mapping"), ("bpy.types.fluiddomainsettings.color_ramp_field_scale*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field-scale"), ("bpy.types.fluiddomainsettings.use_adaptive_timesteps*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-adaptive-timesteps"), ("bpy.types.fluiddomainsettings.use_dissolve_smoke_log*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke-log"), @@ -117,6 +122,8 @@ url_manual_mapping = ( ("bpy.types.brush.use_cloth_pin_simulation_boundary*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-use-cloth-pin-simulation-boundary"), ("bpy.types.brushgpencilsettings.show_fill_boundary*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill-boundary"), ("bpy.types.brushgpencilsettings.use_default_eraser*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-use-default-eraser"), + ("bpy.types.colormanagedsequencercolorspacesettings*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings"), + ("bpy.types.colormanagedviewsettings.view_transform*", "render/color_management.html#bpy-types-colormanagedviewsettings-view-transform"), ("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"), ("bpy.types.fluiddomainsettings.export_manta_script*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-export-manta-script"), ("bpy.types.fluiddomainsettings.fractions_threshold*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-threshold"), @@ -164,6 +171,7 @@ url_manual_mapping = ( ("bpy.types.rigidbodyconstraint.breaking_threshold*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-breaking-threshold"), ("bpy.types.spacedopesheeteditor.show_pose_markers*", "animation/markers.html#bpy-types-spacedopesheeteditor-show-pose-markers"), ("bpy.types.spaceoutliner.use_filter_object_camera*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-camera"), + ("bpy.types.spaceoutliner.use_filter_object_others*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-others"), ("bpy.types.toolsettings.proportional_edit_falloff*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-proportional-edit-falloff"), ("bpy.types.toolsettings.use_edge_path_live_unwrap*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-edge-path-live-unwrap"), ("bpy.types.toolsettings.use_gpencil_draw_additive*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-additive"), @@ -192,12 +200,14 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), + ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"), ("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"), ("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"), ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), ("bpy.types.spacesequenceeditor.show_strip_offset*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-offset"), ("bpy.types.spacesequenceeditor.show_strip_source*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-source"), ("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"), + ("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"), ("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"), @@ -291,6 +301,7 @@ url_manual_mapping = ( ("bpy.types.volumedisplay.interpolation_method*", "modeling/volumes/properties.html#bpy-types-volumedisplay-interpolation-method"), ("bpy.types.clothsettings.use_pressure_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-use-pressure-volume"), ("bpy.types.clothsettings.vertex_group_intern*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-intern"), + ("bpy.types.colormanagedviewsettings.exposure*", "render/color_management.html#bpy-types-colormanagedviewsettings-exposure"), ("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), ("bpy.types.fluiddomainsettings.cfl_condition*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-cfl-condition"), ("bpy.types.fluiddomainsettings.show_velocity*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-show-velocity"), @@ -410,6 +421,7 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.wireframe_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-opacity"), ("bpy.ops.gpencil.active_frames_delete_all*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-active-frames-delete-all"), ("bpy.ops.gpencil.stroke_merge_by_distance*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-merge-by-distance"), + ("bpy.ops.node.collapse_hide_unused_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-collapse-hide-unused-toggle"), ("bpy.ops.object.anim_transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-anim-transforms-to-deltas"), ("bpy.ops.object.convert_proxy_to_override*", "files/linked_libraries/library_overrides.html#bpy-ops-object-convert-proxy-to-override"), ("bpy.ops.object.modifier_copy_to_selected*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy-to-selected"), @@ -421,6 +433,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.show_fill*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill"), ("bpy.types.brushgpencilsettings.uv_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-uv-random"), ("bpy.types.clothsettings.internal_tension*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-tension"), + ("bpy.types.colormanagedviewsettings.gamma*", "render/color_management.html#bpy-types-colormanagedviewsettings-gamma"), ("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"), ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), @@ -475,6 +488,7 @@ url_manual_mapping = ( ("bpy.types.brush.multiplane_scrape_angle*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-multiplane-scrape-angle"), ("bpy.types.clothsettings.internal_spring*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-spring"), ("bpy.types.clothsettings.pressure_factor*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-pressure-factor"), + ("bpy.types.colormanagedviewsettings.look*", "render/color_management.html#bpy-types-colormanagedviewsettings-look"), ("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"), ("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), @@ -513,6 +527,7 @@ url_manual_mapping = ( ("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"), ("bpy.types.workspace.use_filter_by_owner*", "interface/window_system/workspaces.html#bpy-types-workspace-use-filter-by-owner"), + ("bpy.ops.gpencil.image_to_grease_pencil*", "editors/image/editing.html#bpy-ops-gpencil-image-to-grease-pencil"), ("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"), ("bpy.ops.object.multires_rebuild_subdiv*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-rebuild-subdiv"), ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side-of-frame"), @@ -575,6 +590,7 @@ url_manual_mapping = ( ("bpy.types.brush.texture_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-texture-overlay-alpha"), ("bpy.types.brushgpencilsettings.random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-random"), ("bpy.types.clothsettings.target_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-target-volume"), + ("bpy.types.colormanageddisplaysettings*", "render/color_management.html#bpy-types-colormanageddisplaysettings"), ("bpy.types.compositornodebilateralblur*", "compositing/types/filter/bilateral_blur.html#bpy-types-compositornodebilateralblur"), ("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"), ("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"), @@ -660,7 +676,9 @@ url_manual_mapping = ( ("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"), ("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"), ("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"), + ("bpy.types.spaceoutliner.display_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-display-mode"), ("bpy.types.spaceoutliner.filter_state*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-state"), + ("bpy.types.toolsettings.keyframe_type*", "editors/timeline.html#bpy-types-toolsettings-keyframe-type"), ("bpy.types.toolsettings.snap_elements*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-elements"), ("bpy.types.toolsettings.use_snap_self*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-self"), ("bpy.types.viewlayer.active_aov_index*", "render/layers/passes.html#bpy-types-viewlayer-active-aov-index"), @@ -718,6 +736,7 @@ url_manual_mapping = ( ("bpy.types.regionview3d.use_box_clip*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-use-box-clip"), ("bpy.types.rendersettings.use_border*", "render/output/properties/dimensions.html#bpy-types-rendersettings-use-border"), ("bpy.types.rigidbodyconstraint.limit*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-limit"), + ("bpy.types.rigidbodyobject.kinematic*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-kinematic"), ("bpy.types.scene.audio_doppler_speed*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-speed"), ("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"), ("bpy.types.sculpt.detail_type_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-type-method"), @@ -742,7 +761,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.select_interior_faces*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-interior-faces"), ("bpy.ops.mesh.select_similar_region*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar-region"), ("bpy.ops.mesh.tris_convert_to_quads*", "modeling/meshes/editing/face/triangles_quads.html#bpy-ops-mesh-tris-convert-to-quads"), - ("bpy.ops.node.read_fullsamplelayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-fullsamplelayers"), + ("bpy.ops.node.active_preview_toggle*", "modeling/geometry_nodes/introduction.html#bpy-ops-node-active-preview-toggle"), ("bpy.ops.object.datalayout_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data_layout.html#bpy-ops-object-datalayout-transfer"), ("bpy.ops.object.multires_base_apply*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-base-apply"), ("bpy.ops.object.randomize_transform*", "scene_layout/object/editing/transform/randomize.html#bpy-ops-object-randomize-transform"), @@ -752,6 +771,7 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_remove*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-remove"), ("bpy.ops.object.vertex_group_smooth*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-smooth"), ("bpy.ops.outliner.collection_enable*", "editors/outliner/editing.html#bpy-ops-outliner-collection-enable"), + ("bpy.ops.palette.extract_from_image*", "editors/image/editing.html#bpy-ops-palette-extract-from-image"), ("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"), ("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"), ("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"), @@ -766,6 +786,7 @@ url_manual_mapping = ( ("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.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"), ("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill.html#bpy-types-compositornodecolorspill"), @@ -797,6 +818,7 @@ url_manual_mapping = ( ("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"), ("bpy.types.shadernodeoutputmaterial*", "render/shader_nodes/output/material.html#bpy-types-shadernodeoutputmaterial"), ("bpy.types.shadernodetexenvironment*", "render/shader_nodes/textures/environment.html#bpy-types-shadernodetexenvironment"), + ("bpy.types.spacesequenceeditor.show*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show"), ("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/overlays.html#bpy-types-spaceuveditor-uv-opacity"), ("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"), ("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"), @@ -868,6 +890,7 @@ url_manual_mapping = ( ("bpy.types.multiplygpencilmodifier*", "grease_pencil/modifiers/generate/multiple_strokes.html#bpy-types-multiplygpencilmodifier"), ("bpy.types.rendersettings.filepath*", "render/output/properties/output.html#bpy-types-rendersettings-filepath"), ("bpy.types.rendersettings.fps_base*", "render/output/properties/dimensions.html#bpy-types-rendersettings-fps-base"), + ("bpy.types.rigidbodyobject.enabled*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-enabled"), ("bpy.types.sceneeevee.use_overscan*", "render/eevee/render_settings/film.html#bpy-types-sceneeevee-use-overscan"), ("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"), ("bpy.types.shadernodeeeveespecular*", "render/shader_nodes/shader/specular_bsdf.html#bpy-types-shadernodeeeveespecular"), @@ -905,6 +928,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.collection_hide*", "editors/outliner/editing.html#bpy-ops-outliner-collection-hide"), ("bpy.ops.outliner.collection_show*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show"), ("bpy.ops.paint.mask_lasso_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-lasso-gesture"), + ("bpy.ops.rigidbody.mass_calculate*", "physics/rigid_body/editing.html#bpy-ops-rigidbody-mass-calculate"), ("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"), ("bpy.ops.sculpt.detail_flood_fill*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-detail-flood-fill"), ("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"), @@ -979,6 +1003,7 @@ url_manual_mapping = ( ("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.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"), ("bpy.ops.mesh.extrude_faces_move*", "modeling/meshes/editing/face/extrude_individual_faces.html#bpy-ops-mesh-extrude-faces-move"), ("bpy.ops.mesh.faces_shade_smooth*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-smooth"), @@ -987,6 +1012,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.primitive_cube_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-cube-add"), ("bpy.ops.mesh.primitive_grid_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-grid-add"), ("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.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"), @@ -997,6 +1023,8 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_add*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-add"), ("bpy.ops.object.vertex_group_fix*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-fix"), ("bpy.ops.outliner.collection_new*", "editors/outliner/editing.html#bpy-ops-outliner-collection-new"), + ("bpy.ops.outliner.show_hierarchy*", "editors/outliner/editing.html#bpy-ops-outliner-show-hierarchy"), + ("bpy.ops.outliner.show_one_level*", "editors/outliner/editing.html#bpy-ops-outliner-show-one-level"), ("bpy.ops.paint.brush_colors_flip*", "sculpt_paint/texture_paint/tool_settings/brush_settings.html#bpy-ops-paint-brush-colors-flip"), ("bpy.ops.paint.weight_from_bones*", "sculpt_paint/weight_paint/editing.html#bpy-ops-paint-weight-from-bones"), ("bpy.ops.poselib.action_sanitize*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-action-sanitize"), @@ -1011,6 +1039,7 @@ url_manual_mapping = ( ("bpy.ops.uv.shortest_path_select*", "editors/uv/selecting.html#bpy-ops-uv-shortest-path-select"), ("bpy.ops.wm.operator_cheat_sheet*", "advanced/operators.html#bpy-ops-wm-operator-cheat-sheet"), ("bpy.ops.wm.previews_batch_clear*", "files/blend/previews.html#bpy-ops-wm-previews-batch-clear"), + ("bpy.ops.wm.recover_last_session*", "files/blend/open_save.html#bpy-ops-wm-recover-last-session"), ("bpy.types.armature.use_mirror_x*", "animation/armatures/bones/tools/tool_settings.html#bpy-types-armature-use-mirror-x"), ("bpy.types.bakesettings.normal_b*", "render/cycles/baking.html#bpy-types-bakesettings-normal-b"), ("bpy.types.bakesettings.normal_g*", "render/cycles/baking.html#bpy-types-bakesettings-normal-g"), @@ -1046,6 +1075,7 @@ url_manual_mapping = ( ("bpy.types.material.blend_method*", "render/eevee/materials/settings.html#bpy-types-material-blend-method"), ("bpy.types.mirrorgpencilmodifier*", "grease_pencil/modifiers/generate/mirror.html#bpy-types-mirrorgpencilmodifier"), ("bpy.types.movietrackingcamera.k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-k"), + ("bpy.types.node.use_custom_color*", "interface/controls/nodes/sidebar.html#bpy-types-node-use-custom-color"), ("bpy.types.offsetgpencilmodifier*", "grease_pencil/modifiers/deform/offset.html#bpy-types-offsetgpencilmodifier"), ("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"), ("bpy.types.rendersettings.tile_x*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-x"), @@ -1106,6 +1136,7 @@ url_manual_mapping = ( ("bpy.types.compositornoderotate*", "compositing/types/distort/rotate.html#bpy-types-compositornoderotate"), ("bpy.types.compositornodeviewer*", "compositing/types/output/viewer.html#bpy-types-compositornodeviewer"), ("bpy.types.constraint.influence*", "animation/constraints/interface/common.html#bpy-types-constraint-influence"), + ("bpy.types.curve.use_path_clamp*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-clamp"), ("bpy.types.datatransfermodifier*", "modeling/modifiers/modify/data_transfer.html#bpy-types-datatransfermodifier"), ("bpy.types.dynamicpaintmodifier*", "physics/dynamic_paint/index.html#bpy-types-dynamicpaintmodifier"), ("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"), @@ -1118,6 +1149,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodemeshline*", "modeling/geometry_nodes/mesh_primitives/line.html#bpy-types-geometrynodemeshline"), ("bpy.types.gpencillayer.opacity*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-opacity"), ("bpy.types.image.display_aspect*", "editors/image/sidebar.html#bpy-types-image-display-aspect"), + ("bpy.types.keyingsetsall.active*", "editors/timeline.html#bpy-types-keyingsetsall-active"), ("bpy.types.limitscaleconstraint*", "animation/constraints/transform/limit_scale.html#bpy-types-limitscaleconstraint"), ("bpy.types.materialgpencilstyle*", "grease_pencil/materials/index.html#bpy-types-materialgpencilstyle"), ("bpy.types.mesh.use_auto_smooth*", "modeling/meshes/structure.html#bpy-types-mesh-use-auto-smooth"), @@ -1126,6 +1158,7 @@ url_manual_mapping = ( ("bpy.types.object.hide_viewport*", "scene_layout/object/properties/visibility.html#bpy-types-object-hide-viewport"), ("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"), + ("bpy.types.rigidbodyobject.mass*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-mass"), ("bpy.types.scene.background_set*", "scene_layout/scene/properties.html#bpy-types-scene-background-set"), ("bpy.types.sequence.frame_start*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"), ("bpy.types.shadernodebackground*", "render/shader_nodes/shader/background.html#bpy-types-shadernodebackground"), @@ -1159,6 +1192,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"), ("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"), ("bpy.ops.graph.keyframe_insert*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-keyframe-insert"), + ("bpy.ops.image.read_viewlayers*", "editors/image/editing.html#bpy-ops-image-read-viewlayers"), ("bpy.ops.mesh.blend_from_shape*", "modeling/meshes/editing/vertex/blend_shape.html#bpy-ops-mesh-blend-from-shape"), ("bpy.ops.mesh.dissolve_limited*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-limited"), ("bpy.ops.mesh.face_make_planar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-face-make-planar"), @@ -1166,6 +1200,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.faces_shade_flat*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-flat"), ("bpy.ops.mesh.paint_mask_slice*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-paint-mask-slice"), ("bpy.ops.mesh.select_ungrouped*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-ungrouped"), + ("bpy.ops.node.delete_reconnect*", "interface/controls/nodes/editing.html#bpy-ops-node-delete-reconnect"), ("bpy.ops.node.tree_path_parent*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-path-parent"), ("bpy.ops.node.tree_socket_move*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-move"), ("bpy.ops.object.duplicate_move*", "scene_layout/object/editing/duplicate.html#bpy-ops-object-duplicate-move"), @@ -1274,6 +1309,8 @@ url_manual_mapping = ( ("bpy.ops.mesh.delete_edgeloop*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete-edgeloop"), ("bpy.ops.mesh.vertices_smooth*", "modeling/meshes/editing/vertex/smooth_vertices.html#bpy-ops-mesh-vertices-smooth"), ("bpy.ops.nla.make_single_user*", "editors/nla/editing.html#bpy-ops-nla-make-single-user"), + ("bpy.ops.node.clipboard_paste*", "interface/controls/nodes/editing.html#bpy-ops-node-clipboard-paste"), + ("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.data_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data.html#bpy-ops-object-data-transfer"), @@ -1281,6 +1318,8 @@ url_manual_mapping = ( ("bpy.ops.object.select_linked*", "scene_layout/object/selecting.html#bpy-ops-object-select-linked"), ("bpy.ops.object.select_mirror*", "scene_layout/object/selecting.html#bpy-ops-object-select-mirror"), ("bpy.ops.object.select_random*", "scene_layout/object/selecting.html#bpy-ops-object-select-random"), + ("bpy.ops.object.transfer_mode*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-object-transfer-mode"), + ("bpy.ops.outliner.show_active*", "editors/outliner/editing.html#bpy-ops-outliner-show-active"), ("bpy.ops.paint.add_simple_uvs*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-ops-paint-add-simple-uvs"), ("bpy.ops.pose.rigify_generate*", "addons/rigging/rigify/basics.html#bpy-ops-pose-rigify-generate"), ("bpy.ops.preferences.autoexec*", "editors/preferences/save_load.html#bpy-ops-preferences-autoexec"), @@ -1297,6 +1336,7 @@ url_manual_mapping = ( ("bpy.ops.uv.project_from_view*", "modeling/meshes/editing/uv.html#bpy-ops-uv-project-from-view"), ("bpy.ops.wm.blenderkit_logout*", "addons/3d_view/blenderkit.html#bpy-ops-wm-blenderkit-logout"), ("bpy.ops.wm.memory_statistics*", "advanced/operators.html#bpy-ops-wm-memory-statistics"), + ("bpy.ops.wm.recover_auto_save*", "files/blend/open_save.html#bpy-ops-wm-recover-auto-save"), ("bpy.types.adjustmentsequence*", "video_editing/sequencer/strips/adjustment.html#bpy-types-adjustmentsequence"), ("bpy.types.alphaundersequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"), ("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"), @@ -1356,6 +1396,7 @@ url_manual_mapping = ( ("bpy.ops.anim.keyframe_clear*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-clear"), ("bpy.ops.armature.flip_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-flip-names"), ("bpy.ops.armature.select_all*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-all"), + ("bpy.ops.clip.average_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-average-tracks"), ("bpy.ops.clip.refine_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-refine-markers"), ("bpy.ops.clip.select_grouped*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-grouped"), ("bpy.ops.clip.track_to_empty*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-track-to-empty"), @@ -1369,6 +1410,7 @@ url_manual_mapping = ( ("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"), ("bpy.ops.gpencil.trace_image*", "grease_pencil/modes/object/trace_image.html#bpy-ops-gpencil-trace-image"), + ("bpy.ops.image.external_edit*", "editors/image/editing.html#bpy-ops-image-external-edit"), ("bpy.ops.mesh.colors_reverse*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-reverse"), ("bpy.ops.mesh.dissolve_edges*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-edges"), ("bpy.ops.mesh.dissolve_faces*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-faces"), @@ -1383,6 +1425,10 @@ url_manual_mapping = ( ("bpy.ops.mesh.smooth_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-smooth-normals"), ("bpy.ops.nla.action_pushdown*", "editors/nla/tracks.html#bpy-ops-nla-action-pushdown"), ("bpy.ops.nla.tweakmode_enter*", "editors/nla/editing.html#bpy-ops-nla-tweakmode-enter"), + ("bpy.ops.node.clipboard_copy*", "interface/controls/nodes/editing.html#bpy-ops-node-clipboard-copy"), + ("bpy.ops.node.duplicate_move*", "interface/controls/nodes/editing.html#bpy-ops-node-duplicate-move"), + ("bpy.ops.node.options_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-options-toggle"), + ("bpy.ops.node.preview_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-preview-toggle"), ("bpy.ops.object.origin_clear*", "scene_layout/object/editing/clear.html#bpy-ops-object-origin-clear"), ("bpy.ops.object.parent_clear*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-clear"), ("bpy.ops.object.shade_smooth*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-smooth"), @@ -1403,6 +1449,7 @@ url_manual_mapping = ( ("bpy.ops.uv.minimize_stretch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-minimize-stretch"), ("bpy.ops.uv.select_edge_ring*", "editors/uv/selecting.html#bpy-ops-uv-select-edge-ring"), ("bpy.ops.wm.blenderkit_login*", "addons/3d_view/blenderkit.html#bpy-ops-wm-blenderkit-login"), + ("bpy.ops.wm.save_as_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-as-mainfile"), ("bpy.types.alphaoversequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"), ("bpy.types.armatureeditbones*", "animation/armatures/bones/editing/index.html#bpy-types-armatureeditbones"), ("bpy.types.brush.pose_offset*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-offset"), @@ -1642,11 +1689,15 @@ url_manual_mapping = ( ("bpy.ops.mesh.edge_rotate*", "modeling/meshes/editing/edge/rotate_edge.html#bpy-ops-mesh-edge-rotate"), ("bpy.ops.mesh.unsubdivide*", "modeling/meshes/editing/edge/unsubdivide.html#bpy-ops-mesh-unsubdivide"), ("bpy.ops.mesh.uvs_reverse*", "modeling/meshes/uv/editing.html#bpy-ops-mesh-uvs-reverse"), + ("bpy.ops.node.hide_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-toggle"), + ("bpy.ops.node.mute_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-mute-toggle"), ("bpy.ops.object.hide_view*", "scene_layout/object/editing/show_hide.html#bpy-ops-object-hide-view"), ("bpy.ops.object.track_set*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-track-set"), ("bpy.ops.pose.scale_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-scale-clear"), ("bpy.ops.poselib.pose_add*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-add"), ("bpy.ops.scene.view_layer*", "render/layers/introduction.html#bpy-ops-scene-view-layer"), + ("bpy.ops.screen.redo_last*", "interface/undo_redo.html#bpy-ops-screen-redo-last"), + ("bpy.ops.sculpt.mask_init*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-init"), ("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"), ("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"), ("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"), @@ -1659,7 +1710,9 @@ url_manual_mapping = ( ("bpy.ops.uv.snap_selected*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-selected"), ("bpy.ops.view3d.localview*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview"), ("bpy.ops.view3d.view_axis*", "editors/3dview/navigate/viewpoint.html#bpy-ops-view3d-view-axis"), + ("bpy.ops.wm.open_mainfile*", "files/blend/open_save.html#bpy-ops-wm-open-mainfile"), ("bpy.ops.wm.owner_disable*", "interface/window_system/workspaces.html#bpy-ops-wm-owner-disable"), + ("bpy.ops.wm.save_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-mainfile"), ("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"), ("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"), ("bpy.types.curvesmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"), @@ -1715,6 +1768,7 @@ url_manual_mapping = ( ("bpy.ops.nla.clear_scale*", "editors/nla/editing.html#bpy-ops-nla-clear-scale"), ("bpy.ops.nla.mute_toggle*", "editors/nla/editing.html#bpy-ops-nla-mute-toggle"), ("bpy.ops.node.group_make*", "interface/controls/nodes/groups.html#bpy-ops-node-group-make"), + ("bpy.ops.node.links_mute*", "interface/controls/nodes/editing.html#bpy-ops-node-links-mute"), ("bpy.ops.object.armature*", "animation/armatures/index.html#bpy-ops-object-armature"), ("bpy.ops.object.face_map*", "modeling/meshes/properties/object_data.html#bpy-ops-object-face-map"), ("bpy.ops.object.join_uvs*", "scene_layout/object/editing/link_transfer/copy_uvmaps.html#bpy-ops-object-join-uvs"), @@ -1794,6 +1848,8 @@ url_manual_mapping = ( ("bpy.ops.mesh.polybuild*", "modeling/meshes/tools/poly_build.html#bpy-ops-mesh-polybuild"), ("bpy.ops.mesh.subdivide*", "modeling/meshes/editing/edge/subdivide.html#bpy-ops-mesh-subdivide"), ("bpy.ops.mesh.wireframe*", "modeling/meshes/editing/face/wireframe.html#bpy-ops-mesh-wireframe"), + ("bpy.ops.node.link_make*", "interface/controls/nodes/editing.html#bpy-ops-node-link-make"), + ("bpy.ops.node.links_cut*", "interface/controls/nodes/editing.html#bpy-ops-node-links-cut"), ("bpy.ops.object.convert*", "scene_layout/object/editing/convert.html#bpy-ops-object-convert"), ("bpy.ops.object.gpencil*", "grease_pencil/index.html#bpy-ops-object-gpencil"), ("bpy.ops.object.speaker*", "render/output/audio/speaker.html#bpy-ops-object-speaker"), @@ -1815,7 +1871,6 @@ url_manual_mapping = ( ("bpy.types.blendtexture*", "render/materials/legacy_textures/types/blend.html#bpy-types-blendtexture"), ("bpy.types.brush.height*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-height"), ("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"), - ("bpy.types.colormanaged*", "render/color_management.html#bpy-types-colormanaged"), ("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"), ("bpy.types.geometrynode*", "modeling/geometry_nodes/index.html#bpy-types-geometrynode"), ("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"), @@ -1855,6 +1910,8 @@ url_manual_mapping = ( ("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"), ("bpy.ops.gpencil.paste*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-paste"), ("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"), + ("bpy.ops.image.replace*", "editors/image/editing.html#bpy-ops-image-replace"), + ("bpy.ops.image.save_as*", "editors/image/editing.html#bpy-ops-image-save-as"), ("bpy.ops.material.copy*", "render/materials/assignment.html#bpy-ops-material-copy"), ("bpy.ops.mesh.decimate*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-decimate"), ("bpy.ops.mesh.dissolve*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve"), @@ -1907,6 +1964,10 @@ url_manual_mapping = ( ("bpy.ops.graph.sample*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-sample"), ("bpy.ops.graph.smooth*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-smooth"), ("bpy.ops.graph.unbake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-unbake"), + ("bpy.ops.image.invert*", "editors/image/editing.html#bpy-ops-image-invert"), + ("bpy.ops.image.reload*", "editors/image/editing.html#bpy-ops-image-reload"), + ("bpy.ops.image.resize*", "editors/image/editing.html#bpy-ops-image-resize"), + ("bpy.ops.image.unpack*", "editors/image/editing.html#bpy-ops-image-unpack"), ("bpy.ops.material.new*", "render/materials/assignment.html#bpy-ops-material-new"), ("bpy.ops.object.align*", "scene_layout/object/editing/transform/align_objects.html#bpy-ops-object-align"), ("bpy.ops.object.empty*", "modeling/empties.html#bpy-ops-object-empty"), @@ -1921,6 +1982,8 @@ url_manual_mapping = ( ("bpy.types.constraint*", "animation/constraints/index.html#bpy-types-constraint"), ("bpy.types.imagepaint*", "sculpt_paint/texture_paint/index.html#bpy-types-imagepaint"), ("bpy.types.lightprobe*", "render/eevee/light_probes/index.html#bpy-types-lightprobe"), + ("bpy.types.node.color*", "interface/controls/nodes/sidebar.html#bpy-types-node-color"), + ("bpy.types.node.label*", "interface/controls/nodes/sidebar.html#bpy-types-node-label"), ("bpy.types.nodesocket*", "interface/controls/nodes/parts.html#bpy-types-nodesocket"), ("bpy.types.paint.tile*", "sculpt_paint/texture_paint/tool_settings/tiling.html#bpy-types-paint-tile"), ("bpy.types.pointcache*", "physics/baking.html#bpy-types-pointcache"), @@ -1935,6 +1998,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"), ("bpy.ops.mesh.delete*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete"), ("bpy.ops.nla.move_up*", "editors/nla/editing.html#bpy-ops-nla-move-up"), + ("bpy.ops.node.delete*", "interface/controls/nodes/editing.html#bpy-ops-node-delete"), ("bpy.ops.object.bake*", "render/cycles/baking.html#bpy-ops-object-bake"), ("bpy.ops.object.hook*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook"), ("bpy.ops.object.join*", "scene_layout/object/editing/join.html#bpy-ops-object-join"), @@ -1954,6 +2018,7 @@ url_manual_mapping = ( ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifier"), ("bpy.types.freestyle*", "render/freestyle/index.html#bpy-types-freestyle"), ("bpy.types.movieclip*", "movie_clip/index.html#bpy-types-movieclip"), + ("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.spotlight*", "render/lights/light_object.html#bpy-types-spotlight"), @@ -1971,6 +2036,10 @@ url_manual_mapping = ( ("bpy.ops.graph.copy*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-copy"), ("bpy.ops.graph.hide*", "editors/graph_editor/channels.html#bpy-ops-graph-hide"), ("bpy.ops.graph.snap*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap"), + ("bpy.ops.image.flip*", "editors/image/editing.html#bpy-ops-image-flip"), + ("bpy.ops.image.open*", "editors/image/editing.html#bpy-ops-image-open"), + ("bpy.ops.image.pack*", "editors/image/editing.html#bpy-ops-image-pack"), + ("bpy.ops.image.save*", "editors/image/editing.html#bpy-ops-image-save"), ("bpy.ops.image.tile*", "modeling/meshes/uv/workflows/udims.html#bpy-ops-image-tile"), ("bpy.ops.mesh.bevel*", "modeling/meshes/editing/edge/bevel.html#bpy-ops-mesh-bevel"), ("bpy.ops.mesh.inset*", "modeling/meshes/editing/face/inset_faces.html#bpy-ops-mesh-inset"), @@ -2002,6 +2071,7 @@ url_manual_mapping = ( ("bpy.types.spacenla*", "editors/nla/index.html#bpy-types-spacenla"), ("bpy.types.sunlight*", "render/lights/light_object.html#bpy-types-sunlight"), ("bpy.ops.clip.open*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-open"), + ("bpy.ops.image.new*", "editors/image/editing.html#bpy-ops-image-new"), ("bpy.ops.mesh.fill*", "modeling/meshes/editing/face/fill.html#bpy-ops-mesh-fill"), ("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"), ("bpy.ops.mesh.spin*", "modeling/meshes/tools/spin.html#bpy-ops-mesh-spin"), diff --git a/release/scripts/presets/camera/1_colon_2.3_inch.py b/release/scripts/presets/camera/1_colon_2.3_inch.py deleted file mode 100644 index 72548384401..00000000000 --- a/release/scripts/presets/camera/1_colon_2.3_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 6.16 -bpy.context.camera.sensor_height = 4.62 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_inch.py b/release/scripts/presets/camera/1_inch.py new file mode 100644 index 00000000000..72b039fb978 --- /dev/null +++ b/release/scripts/presets/camera/1_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 13.2 +bpy.context.camera.sensor_height = 8.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_1.8_inch.py b/release/scripts/presets/camera/1_slash_1.8_inch.py new file mode 100644 index 00000000000..38e09182de6 --- /dev/null +++ b/release/scripts/presets/camera/1_slash_1.8_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 7.18 +bpy.context.camera.sensor_height = 5.32 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_2.3_inch.py b/release/scripts/presets/camera/1_slash_2.3_inch.py new file mode 100644 index 00000000000..4d55738f4ed --- /dev/null +++ b/release/scripts/presets/camera/1_slash_2.3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 6.17 +bpy.context.camera.sensor_height = 4.55 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/1_colon_2.5_inch.py b/release/scripts/presets/camera/1_slash_2.5_inch.py index 90f60e7d7f0..cbdb6f3cbe0 100644 --- a/release/scripts/presets/camera/1_colon_2.5_inch.py +++ b/release/scripts/presets/camera/1_slash_2.5_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 5.76 bpy.context.camera.sensor_height = 4.29 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_2.7_inch.py b/release/scripts/presets/camera/1_slash_2.7_inch.py new file mode 100644 index 00000000000..5ccfa4ab555 --- /dev/null +++ b/release/scripts/presets/camera/1_slash_2.7_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 5.37 +bpy.context.camera.sensor_height = 4.04 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/iPhone_4.py b/release/scripts/presets/camera/1_slash_3.2_inch.py index 1e43cd11494..1963f7ec048 100644 --- a/release/scripts/presets/camera/iPhone_4.py +++ b/release/scripts/presets/camera/1_slash_3.2_inch.py @@ -1,5 +1,4 @@ import bpy bpy.context.camera.sensor_width = 4.54 bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.lens = 3.85 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/2_colon_3_inch.py b/release/scripts/presets/camera/2_colon_3_inch.py deleted file mode 100644 index 46436970efc..00000000000 --- a/release/scripts/presets/camera/2_colon_3_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 9.6 -bpy.context.camera.sensor_height = 5.4 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/2_slash_3_inch.py b/release/scripts/presets/camera/2_slash_3_inch.py new file mode 100644 index 00000000000..25b46016800 --- /dev/null +++ b/release/scripts/presets/camera/2_slash_3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 8.8 +bpy.context.camera.sensor_height = 6.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/4_colon_3_inch.py b/release/scripts/presets/camera/4_colon_3_inch.py deleted file mode 100644 index 88346c01ef8..00000000000 --- a/release/scripts/presets/camera/4_colon_3_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 17.31 -bpy.context.camera.sensor_height = 12.98 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/APS-C.py b/release/scripts/presets/camera/APS-C.py new file mode 100644 index 00000000000..84e40825248 --- /dev/null +++ b/release/scripts/presets/camera/APS-C.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.6 +bpy.context.camera.sensor_height = 15.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/APS-C_(Canon).py b/release/scripts/presets/camera/APS-C_(Canon).py new file mode 100644 index 00000000000..55f20ce0eac --- /dev/null +++ b/release/scripts/presets/camera/APS-C_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22.30 +bpy.context.camera.sensor_height = 14.90 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Canon_APS-H.py b/release/scripts/presets/camera/APS-H_(Canon).py index d3b61d1aa46..d63f733280b 100644 --- a/release/scripts/presets/camera/Canon_APS-H.py +++ b/release/scripts/presets/camera/APS-H_(Canon).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 27.90 bpy.context.camera.sensor_height = 18.60 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_16mm.py b/release/scripts/presets/camera/Analog_16mm.py new file mode 100644 index 00000000000..aa98eaf2408 --- /dev/null +++ b/release/scripts/presets/camera/Analog_16mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 10.26 +bpy.context.camera.sensor_height = 7.49 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_35mm.py b/release/scripts/presets/camera/Analog_35mm.py new file mode 100644 index 00000000000..a0dee1f0166 --- /dev/null +++ b/release/scripts/presets/camera/Analog_35mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22 +bpy.context.camera.sensor_height = 16 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_65mm.py b/release/scripts/presets/camera/Analog_65mm.py new file mode 100644 index 00000000000..8de91ac0ee3 --- /dev/null +++ b/release/scripts/presets/camera/Analog_65mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 52.45 +bpy.context.camera.sensor_height = 23.01 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_IMAX.py b/release/scripts/presets/camera/Analog_IMAX.py new file mode 100644 index 00000000000..5a445f3de8c --- /dev/null +++ b/release/scripts/presets/camera/Analog_IMAX.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 71.41 +bpy.context.camera.sensor_height = 52.63 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_Super_16.py b/release/scripts/presets/camera/Analog_Super_16.py new file mode 100644 index 00000000000..a340a31dc25 --- /dev/null +++ b/release/scripts/presets/camera/Analog_Super_16.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.35 +bpy.context.camera.sensor_height = 7.42 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Super_35_Film.py b/release/scripts/presets/camera/Analog_Super_35.py index b22ff545c68..3c8f1837253 100644 --- a/release/scripts/presets/camera/Super_35_Film.py +++ b/release/scripts/presets/camera/Analog_Super_35.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 24.89 bpy.context.camera.sensor_height = 18.66 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Arri_Alexa.py b/release/scripts/presets/camera/Arri_Alexa.py deleted file mode 100644 index 6a6cdfee12b..00000000000 --- a/release/scripts/presets/camera/Arri_Alexa.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.760 -bpy.context.camera.sensor_height = 13.365 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Arri_Alexa_65.py b/release/scripts/presets/camera/Arri_Alexa_65.py new file mode 100644 index 00000000000..b1467709949 --- /dev/null +++ b/release/scripts/presets/camera/Arri_Alexa_65.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 54.12 +bpy.context.camera.sensor_height = 25.58 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Arri_Alexa_LF.py b/release/scripts/presets/camera/Arri_Alexa_LF.py new file mode 100644 index 00000000000..1cde94fce8d --- /dev/null +++ b/release/scripts/presets/camera/Arri_Alexa_LF.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36.70 +bpy.context.camera.sensor_height = 25.54 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py b/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py deleted file mode 100644 index 6fde30756da..00000000000 --- a/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 15.81 -bpy.context.camera.sensor_height = 8.88 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py index bb2b172919e..260bfbaf94f 100644 --- a/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py +++ b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 12.48 bpy.context.camera.sensor_height = 7.02 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py new file mode 100644 index 00000000000..dc057397828 --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 18.96 +bpy.context.camera.sensor_height = 10.00 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py new file mode 100644 index 00000000000..a483f3d5f98 --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.10 +bpy.context.camera.sensor_height = 12.99 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py b/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py deleted file mode 100644 index dbc12c5aa68..00000000000 --- a/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 21.12 -bpy.context.camera.sensor_height = 11.88 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py new file mode 100644 index 00000000000..c71e42d72d3 --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.34 +bpy.context.camera.sensor_height = 14.25 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Blender.py b/release/scripts/presets/camera/Blender.py deleted file mode 100644 index ca4906fbb39..00000000000 --- a/release/scripts/presets/camera/Blender.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 32 -bpy.context.camera.sensor_height = 18 -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/Canon_1100D.py b/release/scripts/presets/camera/Canon_1100D.py deleted file mode 100644 index e665e9e95d5..00000000000 --- a/release/scripts/presets/camera/Canon_1100D.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 22.2 -bpy.context.camera.sensor_height = 14.7 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Canon_APS-C.py b/release/scripts/presets/camera/Canon_APS-C.py deleted file mode 100644 index 95108b2187f..00000000000 --- a/release/scripts/presets/camera/Canon_APS-C.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 22.3 -bpy.context.camera.sensor_height = 14.9 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Canon_C300.py b/release/scripts/presets/camera/Canon_C300.py deleted file mode 100644 index e22af779854..00000000000 --- a/release/scripts/presets/camera/Canon_C300.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 24.4 -bpy.context.camera.sensor_height = 13.5 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Foveon_(Sigma).py b/release/scripts/presets/camera/Foveon_(Sigma).py new file mode 100644 index 00000000000..e6a1a0ed344 --- /dev/null +++ b/release/scripts/presets/camera/Foveon_(Sigma).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 20.70 +bpy.context.camera.sensor_height = 13.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Full_Frame_35mm_Camera.py b/release/scripts/presets/camera/Fullframe.py index c8017331b28..95fb4afc10b 100644 --- a/release/scripts/presets/camera/Full_Frame_35mm_Camera.py +++ b/release/scripts/presets/camera/Fullframe.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 36 bpy.context.camera.sensor_height = 24 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/GoPro_Hero3_Black.py b/release/scripts/presets/camera/GoPro_Hero3_Black.py deleted file mode 100644 index e294f802a02..00000000000 --- a/release/scripts/presets/camera/GoPro_Hero3_Black.py +++ /dev/null @@ -1,6 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 6.16 -bpy.context.camera.sensor_height = 4.62 -bpy.context.camera.lens = 2.77 - -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/GoPro_Hero3_Silver.py b/release/scripts/presets/camera/GoPro_Hero3_Silver.py deleted file mode 100644 index 247bd7c4aaf..00000000000 --- a/release/scripts/presets/camera/GoPro_Hero3_Silver.py +++ /dev/null @@ -1,6 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 5.371 -bpy.context.camera.sensor_height = 4.035 -bpy.context.camera.lens = 2.77 - -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/MFT.py b/release/scripts/presets/camera/MFT.py new file mode 100644 index 00000000000..bc0dd49baa8 --- /dev/null +++ b/release/scripts/presets/camera/MFT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 17.3 +bpy.context.camera.sensor_height = 13.0 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Medium-format_(Hasselblad).py b/release/scripts/presets/camera/Medium-format_(Hasselblad).py new file mode 100644 index 00000000000..e9b16024b79 --- /dev/null +++ b/release/scripts/presets/camera/Medium-format_(Hasselblad).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 44 +bpy.context.camera.sensor_height = 33 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Nexus_5.py b/release/scripts/presets/camera/Nexus_5.py deleted file mode 100644 index 36e741cbba5..00000000000 --- a/release/scripts/presets/camera/Nexus_5.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.5 -bpy.context.camera.sensor_height = 3.37 -bpy.context.camera.lens = 3.91 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Nikon_D3100.py b/release/scripts/presets/camera/Nikon_D3100.py deleted file mode 100644 index b4ceb3aa721..00000000000 --- a/release/scripts/presets/camera/Nikon_D3100.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.1 -bpy.context.camera.sensor_height = 15.4 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Nikon_DX.py b/release/scripts/presets/camera/Nikon_DX.py deleted file mode 100644 index dbe9e7fcc18..00000000000 --- a/release/scripts/presets/camera/Nikon_DX.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.6 -bpy.context.camera.sensor_height = 15.8 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Panasonic_AG-HVX200.py b/release/scripts/presets/camera/Panasonic_AG-HVX200.py deleted file mode 100644 index 71ad3c3a161..00000000000 --- a/release/scripts/presets/camera/Panasonic_AG-HVX200.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.68 -bpy.context.camera.sensor_height = 2.633 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Panasonic_LX2.py b/release/scripts/presets/camera/Panasonic_LX2.py deleted file mode 100644 index d66e02e32d4..00000000000 --- a/release/scripts/presets/camera/Panasonic_LX2.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 8.5 -bpy.context.camera.sensor_height = 4.78 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/RED_Dragon_5K.py b/release/scripts/presets/camera/RED_Dragon_5K.py new file mode 100644 index 00000000000..fa95a98f8c4 --- /dev/null +++ b/release/scripts/presets/camera/RED_Dragon_5K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.60 +bpy.context.camera.sensor_height = 13.5 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/RED_Dragon_6K.py b/release/scripts/presets/camera/RED_Dragon_6K.py new file mode 100644 index 00000000000..80f7ad1bbb8 --- /dev/null +++ b/release/scripts/presets/camera/RED_Dragon_6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 30.70 +bpy.context.camera.sensor_height = 15.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/RED_Helium_8K.py b/release/scripts/presets/camera/RED_Helium_8K.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/camera/RED_Helium_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/RED_Monstro_8K.py b/release/scripts/presets/camera/RED_Monstro_8K.py new file mode 100644 index 00000000000..86c382624ab --- /dev/null +++ b/release/scripts/presets/camera/RED_Monstro_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 40.96 +bpy.context.camera.sensor_height = 21.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/Red_Epic.py b/release/scripts/presets/camera/Red_Epic.py deleted file mode 100644 index 5d71a69a33d..00000000000 --- a/release/scripts/presets/camera/Red_Epic.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 30.0 -bpy.context.camera.sensor_height = 15.0 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Red_One_2K.py b/release/scripts/presets/camera/Red_One_2K.py deleted file mode 100644 index 894aedcf9ea..00000000000 --- a/release/scripts/presets/camera/Red_One_2K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 11.1 -bpy.context.camera.sensor_height = 6.24 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Red_One_3K.py b/release/scripts/presets/camera/Red_One_3K.py deleted file mode 100644 index 9ac84c1485a..00000000000 --- a/release/scripts/presets/camera/Red_One_3K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 16.65 -bpy.context.camera.sensor_height = 9.36 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Red_One_4K.py b/release/scripts/presets/camera/Red_One_4K.py deleted file mode 100644 index 067322cd07e..00000000000 --- a/release/scripts/presets/camera/Red_One_4K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 22.2 -bpy.context.camera.sensor_height = 12.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Samsung_Galaxy_S3.py b/release/scripts/presets/camera/Samsung_Galaxy_S3.py deleted file mode 100644 index 23eaea7cd27..00000000000 --- a/release/scripts/presets/camera/Samsung_Galaxy_S3.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.8 -bpy.context.camera.sensor_height = 3.6 -bpy.context.camera.lens = 3.70 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Samsung_Galaxy_S4.py b/release/scripts/presets/camera/Samsung_Galaxy_S4.py deleted file mode 100644 index cc929d26dac..00000000000 --- a/release/scripts/presets/camera/Samsung_Galaxy_S4.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.8 -bpy.context.camera.sensor_height = 3.6 -bpy.context.camera.lens = 4.20 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Sony_A55.py b/release/scripts/presets/camera/Sony_A55.py deleted file mode 100644 index 0468deb6d4c..00000000000 --- a/release/scripts/presets/camera/Sony_A55.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.4 -bpy.context.camera.sensor_height = 15.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Sony_EX1.py b/release/scripts/presets/camera/Sony_EX1.py deleted file mode 100644 index 3c6b235f21e..00000000000 --- a/release/scripts/presets/camera/Sony_EX1.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 6.97 -bpy.context.camera.sensor_height = 3.92 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Sony_F65.py b/release/scripts/presets/camera/Sony_F65.py deleted file mode 100644 index e62b3511836..00000000000 --- a/release/scripts/presets/camera/Sony_F65.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 24.33 -bpy.context.camera.sensor_height = 12.83 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Super_16_Film.py b/release/scripts/presets/camera/Super_16_Film.py deleted file mode 100644 index 4ca397a7e27..00000000000 --- a/release/scripts/presets/camera/Super_16_Film.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 12.52 -bpy.context.camera.sensor_height = 7.41 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/iPhone_5.py b/release/scripts/presets/camera/iPhone_5.py deleted file mode 100644 index a6b6bbc2ec5..00000000000 --- a/release/scripts/presets/camera/iPhone_5.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.54 -bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.lens = 4.10 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index eb66c961472..222ee43432f 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -103,8 +103,8 @@ class Prefs(bpy.types.KeyConfigPreferences): v3d_tilde_action: EnumProperty( name="Tilde Action", items=( - ('VIEW', "Navigate", - "View operations (useful for keyboards without a numpad)", + ('OBJECT_SWITCH', "Object Switch", + "Switch the active object under the cursor (when not in object mode)", 0), ('GIZMO', "Gizmos", "Control transform gizmos", @@ -113,7 +113,7 @@ class Prefs(bpy.types.KeyConfigPreferences): description=( "Action when 'Tilde' is pressed" ), - default='VIEW', + default='OBJECT_SWITCH', update=update_fn, ) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 6a33bc60cba..27abaa61339 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1080,12 +1080,7 @@ def km_view3d(params): {"properties": [("use_all_regions", True), ("center", False)]}), ("view3d.view_all", {"type": 'C', "value": 'PRESS', "shift": True}, {"properties": [("center", True)]}), - op_menu_pie( - "VIEW3D_MT_view_pie" if params.v3d_tilde_action == 'VIEW' else "VIEW3D_MT_transform_gizmo_pie", - {"type": 'ACCENT_GRAVE', "value": params.pie_value}, - ), - *(() if not params.use_pie_click_drag else - (("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)), + op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}), ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), # Numpad views. ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), @@ -1329,6 +1324,32 @@ def km_view3d(params): op_tool_cycle("builtin.select_box", {"type": 'W', "value": 'PRESS'}), ]) + # Tilda key. + if params.use_pie_click_drag: + items.extend([ + ("object.transfer_mode", + {"type": 'ACCENT_GRAVE', "value": 'CLICK' if params.use_pie_click_drag else 'PRESS'}, + None), + op_menu_pie( + "VIEW3D_MT_transform_gizmo_pie", + {"type": 'ACCENT_GRAVE', "value": 'CLICK_DRAG'}, + ) + ]) + else: + if params.v3d_tilde_action == 'OBJECT_SWITCH': + items.append( + ("object.transfer_mode", + {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, + {"properties": [("use_eyedropper", False)]}) + ) + else: + items.append( + op_menu_pie( + "VIEW3D_MT_transform_gizmo_pie", + {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, + ) + ) + return keymap @@ -2525,6 +2546,7 @@ def km_sequencercommon(params): {"properties": [("data_path", 'scene.sequence_editor.show_overlay')]}), ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}), + ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ]) if params.select_mouse == 'LEFTMOUSE' and not params.legacy: @@ -2565,7 +2587,6 @@ def km_sequencer(params): ("sequencer.reload", {"type": 'R', "value": 'PRESS', "alt": True}, None), ("sequencer.reload", {"type": 'R', "value": 'PRESS', "shift": True, "alt": True}, {"properties": [("adjust_length", True)]}), - ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("sequencer.offset_clear", {"type": 'O', "value": 'PRESS', "alt": True}, None), ("sequencer.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("sequencer.delete", {"type": 'X', "value": 'PRESS'}, None), @@ -2573,7 +2594,7 @@ def km_sequencer(params): ("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True, "shift": True}, - {"properties": [("keep_offset", True)]}), + {"properties": [("keep_offset", True)]}), ("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None), ("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None), ("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), @@ -4486,8 +4507,6 @@ def km_sculpt(params): ) items.extend([ - # Transfer Sculpt Mode (release to avoid conflict with grease pencil drawing). - ("object.transfer_mode", {"type": 'D', "value": 'RELEASE'}, None), # Brush strokes ("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": [("mode", 'NORMAL')]}), diff --git a/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py b/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py deleted file mode 100644 index 9fcd40fbb65..00000000000 --- a/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 6.16 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py b/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py deleted file mode 100644 index 2f064e59838..00000000000 --- a/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 5.76 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/1_inch.py b/release/scripts/presets/tracking_camera/1_inch.py new file mode 100644 index 00000000000..72b039fb978 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 13.2 +bpy.context.camera.sensor_height = 8.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py new file mode 100644 index 00000000000..38e09182de6 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 7.18 +bpy.context.camera.sensor_height = 5.32 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py new file mode 100644 index 00000000000..4d55738f4ed --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 6.17 +bpy.context.camera.sensor_height = 4.55 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/GoPro_Hero3_White.py b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py index 948f838f5d6..cbdb6f3cbe0 100644 --- a/release/scripts/presets/camera/GoPro_Hero3_White.py +++ b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py @@ -1,6 +1,4 @@ import bpy bpy.context.camera.sensor_width = 5.76 bpy.context.camera.sensor_height = 4.29 -bpy.context.camera.lens = 2.77 - -bpy.context.camera.sensor_fit = 'AUTO' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py new file mode 100644 index 00000000000..5ccfa4ab555 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 5.37 +bpy.context.camera.sensor_height = 4.04 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/camera/iPhone_4S.py b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py index 1139b7395b5..1963f7ec048 100644 --- a/release/scripts/presets/camera/iPhone_4S.py +++ b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py @@ -1,5 +1,4 @@ import bpy bpy.context.camera.sensor_width = 4.54 bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.lens = 4.28 -bpy.context.camera.sensor_fit = 'HORIZONTAL' +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/2__colon__3_inch.py b/release/scripts/presets/tracking_camera/2__colon__3_inch.py deleted file mode 100644 index 8936e627d77..00000000000 --- a/release/scripts/presets/tracking_camera/2__colon__3_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 9.6 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/2_slash_3_inch.py b/release/scripts/presets/tracking_camera/2_slash_3_inch.py new file mode 100644 index 00000000000..25b46016800 --- /dev/null +++ b/release/scripts/presets/tracking_camera/2_slash_3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 8.8 +bpy.context.camera.sensor_height = 6.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/4__colon__3_inch.py b/release/scripts/presets/tracking_camera/4__colon__3_inch.py deleted file mode 100644 index 2317715e1b4..00000000000 --- a/release/scripts/presets/tracking_camera/4__colon__3_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 17.31 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/APS-C.py b/release/scripts/presets/tracking_camera/APS-C.py new file mode 100644 index 00000000000..84e40825248 --- /dev/null +++ b/release/scripts/presets/tracking_camera/APS-C.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.6 +bpy.context.camera.sensor_height = 15.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/APS-C_(Canon).py b/release/scripts/presets/tracking_camera/APS-C_(Canon).py new file mode 100644 index 00000000000..55f20ce0eac --- /dev/null +++ b/release/scripts/presets/tracking_camera/APS-C_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22.30 +bpy.context.camera.sensor_height = 14.90 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/APS-H_(Canon).py b/release/scripts/presets/tracking_camera/APS-H_(Canon).py new file mode 100644 index 00000000000..d63f733280b --- /dev/null +++ b/release/scripts/presets/tracking_camera/APS-H_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 27.90 +bpy.context.camera.sensor_height = 18.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_16mm.py b/release/scripts/presets/tracking_camera/Analog_16mm.py new file mode 100644 index 00000000000..aa98eaf2408 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_16mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 10.26 +bpy.context.camera.sensor_height = 7.49 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_35mm.py b/release/scripts/presets/tracking_camera/Analog_35mm.py new file mode 100644 index 00000000000..a0dee1f0166 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_35mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22 +bpy.context.camera.sensor_height = 16 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_65mm.py b/release/scripts/presets/tracking_camera/Analog_65mm.py new file mode 100644 index 00000000000..8de91ac0ee3 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_65mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 52.45 +bpy.context.camera.sensor_height = 23.01 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_IMAX.py b/release/scripts/presets/tracking_camera/Analog_IMAX.py new file mode 100644 index 00000000000..5a445f3de8c --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_IMAX.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 71.41 +bpy.context.camera.sensor_height = 52.63 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_Super_16.py b/release/scripts/presets/tracking_camera/Analog_Super_16.py new file mode 100644 index 00000000000..a340a31dc25 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_Super_16.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.35 +bpy.context.camera.sensor_height = 7.42 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_Super_35.py b/release/scripts/presets/tracking_camera/Analog_Super_35.py new file mode 100644 index 00000000000..3c8f1837253 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_Super_35.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 24.89 +bpy.context.camera.sensor_height = 18.66 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa.py b/release/scripts/presets/tracking_camera/Arri_Alexa.py deleted file mode 100644 index ded361ec965..00000000000 --- a/release/scripts/presets/tracking_camera/Arri_Alexa.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.76 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_65.py b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py new file mode 100644 index 00000000000..b1467709949 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 54.12 +bpy.context.camera.sensor_height = 25.58 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py new file mode 100644 index 00000000000..1cde94fce8d --- /dev/null +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36.70 +bpy.context.camera.sensor_height = 25.54 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py b/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py deleted file mode 100644 index f84d0a19d22..00000000000 --- a/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 15.81 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py new file mode 100644 index 00000000000..260bfbaf94f --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.48 +bpy.context.camera.sensor_height = 7.02 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py new file mode 100644 index 00000000000..dc057397828 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 18.96 +bpy.context.camera.sensor_height = 10.00 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py new file mode 100644 index 00000000000..a483f3d5f98 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.10 +bpy.context.camera.sensor_height = 12.99 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py deleted file mode 100644 index a9c81f47c21..00000000000 --- a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 12.48 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py deleted file mode 100644 index d644d2a26c9..00000000000 --- a/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 21.12 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py new file mode 100644 index 00000000000..c71e42d72d3 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.34 +bpy.context.camera.sensor_height = 14.25 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blender.py b/release/scripts/presets/tracking_camera/Blender.py deleted file mode 100644 index 507cedac4fc..00000000000 --- a/release/scripts/presets/tracking_camera/Blender.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 32.0 -camera.units = 'MILLIMETERS' -camera.focal_length = 35.0 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_1100D.py b/release/scripts/presets/tracking_camera/Canon_1100D.py deleted file mode 100644 index 96d6d456337..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_1100D.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 22.2 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_APS-C.py b/release/scripts/presets/tracking_camera/Canon_APS-C.py deleted file mode 100644 index cc4da545272..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_APS-C.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 22.3 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_APS-H.py b/release/scripts/presets/tracking_camera/Canon_APS-H.py deleted file mode 100644 index 853edd5dcba..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_APS-H.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 27.90 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_C300.py b/release/scripts/presets/tracking_camera/Canon_C300.py deleted file mode 100644 index 809f8f432f8..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_C300.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 24.4 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Foveon_(Sigma).py b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py new file mode 100644 index 00000000000..e6a1a0ed344 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 20.70 +bpy.context.camera.sensor_height = 13.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py b/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py deleted file mode 100644 index 0f3da0b4d72..00000000000 --- a/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 36 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Fullframe.py b/release/scripts/presets/tracking_camera/Fullframe.py new file mode 100644 index 00000000000..95fb4afc10b --- /dev/null +++ b/release/scripts/presets/tracking_camera/Fullframe.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36 +bpy.context.camera.sensor_height = 24 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py deleted file mode 100644 index 29851352284..00000000000 --- a/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 6.16 -camera.units = 'MILLIMETERS' -camera.focal_length = 2.77 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py deleted file mode 100644 index 9e08cf283a7..00000000000 --- a/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 5.371 -camera.units = 'MILLIMETERS' -camera.focal_length = 2.77 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py deleted file mode 100644 index 6b1f9d97e81..00000000000 --- a/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 5.76 -camera.units = 'MILLIMETERS' -camera.focal_length = 2.77 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/MFT.py b/release/scripts/presets/tracking_camera/MFT.py new file mode 100644 index 00000000000..bc0dd49baa8 --- /dev/null +++ b/release/scripts/presets/tracking_camera/MFT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 17.3 +bpy.context.camera.sensor_height = 13.0 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py new file mode 100644 index 00000000000..e9b16024b79 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 44 +bpy.context.camera.sensor_height = 33 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Nexus_5.py b/release/scripts/presets/tracking_camera/Nexus_5.py deleted file mode 100644 index 172c8e93bfd..00000000000 --- a/release/scripts/presets/tracking_camera/Nexus_5.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.5 -camera.units = 'MILLIMETERS' -camera.focal_length = 3.91 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Nikon_D3100.py b/release/scripts/presets/tracking_camera/Nikon_D3100.py deleted file mode 100644 index 44646f8b112..00000000000 --- a/release/scripts/presets/tracking_camera/Nikon_D3100.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.1 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Nikon_DX.py b/release/scripts/presets/tracking_camera/Nikon_DX.py deleted file mode 100644 index 8d9e3505e3f..00000000000 --- a/release/scripts/presets/tracking_camera/Nikon_DX.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.6 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py b/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py deleted file mode 100644 index 49cc71fa5da..00000000000 --- a/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.68 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1.5 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Panasonic_LX2.py b/release/scripts/presets/tracking_camera/Panasonic_LX2.py deleted file mode 100644 index f9ffcb8ec03..00000000000 --- a/release/scripts/presets/tracking_camera/Panasonic_LX2.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 8.5 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_5K.py b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py new file mode 100644 index 00000000000..fa95a98f8c4 --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.60 +bpy.context.camera.sensor_height = 13.5 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_6K.py b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py new file mode 100644 index 00000000000..80f7ad1bbb8 --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 30.70 +bpy.context.camera.sensor_height = 15.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/RED_Helium_8K.py b/release/scripts/presets/tracking_camera/RED_Helium_8K.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Helium_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/RED_Monstro_8K.py b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py new file mode 100644 index 00000000000..86c382624ab --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 40.96 +bpy.context.camera.sensor_height = 21.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Red_Epic.py b/release/scripts/presets/tracking_camera/Red_Epic.py deleted file mode 100644 index c0790e6baed..00000000000 --- a/release/scripts/presets/tracking_camera/Red_Epic.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 30.0 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Red_One_2K.py b/release/scripts/presets/tracking_camera/Red_One_2K.py deleted file mode 100644 index fa9585b5e08..00000000000 --- a/release/scripts/presets/tracking_camera/Red_One_2K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 11.1 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Red_One_3K.py b/release/scripts/presets/tracking_camera/Red_One_3K.py deleted file mode 100644 index 5a1b7472109..00000000000 --- a/release/scripts/presets/tracking_camera/Red_One_3K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 16.65 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Red_One_4K.py b/release/scripts/presets/tracking_camera/Red_One_4K.py deleted file mode 100644 index 96d6d456337..00000000000 --- a/release/scripts/presets/tracking_camera/Red_One_4K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 22.2 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py b/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py deleted file mode 100644 index d10994e45f5..00000000000 --- a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.8 -camera.units = 'MILLIMETERS' -camera.focal_length = 3.70 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py b/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py deleted file mode 100644 index c5fef80b3de..00000000000 --- a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.8 -camera.units = 'MILLIMETERS' -camera.focal_length = 4.2 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Sony_A55.py b/release/scripts/presets/tracking_camera/Sony_A55.py deleted file mode 100644 index 26920d06f94..00000000000 --- a/release/scripts/presets/tracking_camera/Sony_A55.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.4 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Sony_EX1.py b/release/scripts/presets/tracking_camera/Sony_EX1.py deleted file mode 100644 index 2b99c91d221..00000000000 --- a/release/scripts/presets/tracking_camera/Sony_EX1.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 6.97 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Sony_F65.py b/release/scripts/presets/tracking_camera/Sony_F65.py deleted file mode 100644 index 7da93fd5d47..00000000000 --- a/release/scripts/presets/tracking_camera/Sony_F65.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 24.33 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Super_16.py b/release/scripts/presets/tracking_camera/Super_16.py deleted file mode 100644 index e94da9a99ba..00000000000 --- a/release/scripts/presets/tracking_camera/Super_16.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 12.52 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Super_35.py b/release/scripts/presets/tracking_camera/Super_35.py deleted file mode 100644 index e07edc3a22c..00000000000 --- a/release/scripts/presets/tracking_camera/Super_35.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 24.89 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/iPhone_4.py b/release/scripts/presets/tracking_camera/iPhone_4.py deleted file mode 100644 index 220e5e08147..00000000000 --- a/release/scripts/presets/tracking_camera/iPhone_4.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.54 -camera.units = 'MILLIMETERS' -camera.focal_length = 3.85 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/iPhone_4S.py b/release/scripts/presets/tracking_camera/iPhone_4S.py deleted file mode 100644 index 686cffc8f99..00000000000 --- a/release/scripts/presets/tracking_camera/iPhone_4S.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.54 -camera.units = 'MILLIMETERS' -camera.focal_length = 4.28 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/iPhone_5.py b/release/scripts/presets/tracking_camera/iPhone_5.py deleted file mode 100644 index d8e05da8425..00000000000 --- a/release/scripts/presets/tracking_camera/iPhone_5.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.54 -camera.units = 'MILLIMETERS' -camera.focal_length = 4.10 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index 0c7a2a01b7a..71ef89a066b 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -42,8 +42,8 @@ def geometry_node_group_empty_new(): def geometry_modifier_poll(context): ob = context.object - # Test object support for geometry node modifier (No volume, curve, or hair object support yet) - if not ob or ob.type not in {'MESH', 'POINTCLOUD'}: + # Test object support for geometry node modifier (No curve, or hair object support yet) + if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME'}: return False return True diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 3969386bad7..dd84dfa2df8 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -153,7 +153,7 @@ class PREFERENCES_OT_copy_prev(Operator): def execute(self, _context): import shutil - shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True) + shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True, symlinks=True) # reload preferences and recent-files.txt bpy.ops.wm.read_userpref() diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 29c17711c2a..6ba8750e9df 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -628,7 +628,7 @@ class LightMapPack(Operator): name="New Image", description=( "Assign new images for every mesh (only one if " - "shared tex space enabled)" + "Share Texture Space is enabled)" ), default=False, ) diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index e835e577953..a88def34767 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -245,6 +245,7 @@ class ConstraintButtonsPanel: sub.prop(con, "max_z", text="Max") row.label(icon='BLANK1') + layout.prop(con, "euler_order", text="Order") layout.prop(con, "use_transform_limit") self.space_template(layout, con, target=False, owner=True) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 55a49878b71..2e89ddcb1d4 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -407,7 +407,7 @@ class AnnotationDataPanel: bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): - if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}: + if context.space_data.type not in {'VIEW_3D', 'TOPBAR', 'SEQUENCE_EDITOR'}: self.layout.prop(context.space_data, "show_annotation", text="") def draw(self, context): @@ -857,6 +857,10 @@ class GreasePencilLayerRelationsPanel: col = layout.row(align=True) col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer") + col = layout.row(align=True) + # Only enable this property when a view layer is selected. + col.enabled = bool(gpl.viewlayer_render) + col.prop(gpl, "use_viewlayer_masks") class GreasePencilLayerDisplayPanel: diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index f3462dfb35d..4bfd2fd32b0 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1235,7 +1235,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(brush, "size", text="Radius") row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') - if gp_settings.use_pressure and context.area.type == 'PROPERTIES': + if gp_settings.use_pressure and not compact: col = layout.column() col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, use_negative_slope=True) @@ -1244,7 +1244,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(gp_settings, "pen_strength", slider=True) row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') - if gp_settings.use_strength_pressure and context.area.type == 'PROPERTIES': + if gp_settings.use_strength_pressure and not compact: col = layout.column() col.template_curve_mapping(gp_settings, "curve_strength", brush=True, use_negative_slope=True) diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 89ce742b81e..1208ca0a64a 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -660,8 +660,12 @@ class NODE_PT_quality(bpy.types.Panel): snode = context.space_data tree = snode.node_tree + prefs = bpy.context.preferences col = layout.column() + if prefs.experimental.use_full_frame_compositor: + col.prop(tree, "execution_mode") + col.prop(tree, "render_quality", text="Render") col.prop(tree, "edit_quality", text="Edit") col.prop(tree, "chunk_size") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index b24b6e84939..07d9b0ee1f8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1381,7 +1381,6 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): col = layout.column() col.prop(strip, "filepath", text="") col.prop(strip.colorspace_settings, "name", text="Color Space") - col.prop(strip, "mpeg_preseek") col.prop(strip, "stream_index") col.prop(strip, "use_deinterlace") @@ -1398,8 +1397,8 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): box.template_image_stereo_3d(strip.stereo_3d_format) # Resolution. - col = layout.column(align=True) - col = col.box() + col = layout.box() + col = col.column(align=True) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' split.label(text="Resolution") @@ -1409,6 +1408,14 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): split.label(text="%dx%d" % size, translate=False) else: split.label(text="None") + #FPS + if elem.orig_fps: + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="FPS") + split.alignment = 'LEFT' + split.label(text="%.2f" % elem.orig_fps, translate=False) + class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): @@ -1938,7 +1945,7 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): layout.prop(proxy, "use_overwrite") col = layout.column() - col.prop(proxy, "quality", text="Build JPEG Quality") + col.prop(proxy, "quality", text="Quality") if strip.type == 'MOVIE': col = layout.column() diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 1e52142c85c..ce1c401b14b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -164,7 +164,7 @@ class _defs_annotate: gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - if context.space_data.type == 'VIEW_3D': + if context.space_data.type in {'VIEW_3D', 'SEQUENCE_EDITOR'}: if region_type == 'TOOL_HEADER': sub = layout.split(align=True, factor=0.5) sub.ui_units_x = 6.5 @@ -206,14 +206,22 @@ class _defs_annotate: col = layout.row().column(align=True) col.prop(props, "arrowstyle_start", text="Style Start") col.prop(props, "arrowstyle_end", text="End") - elif tool.idname == "builtin.annotate" and region_type != 'TOOL_HEADER': - layout.separator() + elif tool.idname == "builtin.annotate": props = tool.operator_properties("gpencil.annotate") - layout.prop(props, "use_stabilizer", text="Stabilize Stroke") - col = layout.column(align=False) - col.active = props.use_stabilizer - col.prop(props, "stabilizer_radius", text="Radius", slider=True) - col.prop(props, "stabilizer_factor", text="Factor", slider=True) + if region_type == 'TOOL_HEADER': + row = layout.row() + row.prop(props, "use_stabilizer", text="Stabilize Stroke") + subrow = layout.row(align=False) + subrow.active = props.use_stabilizer + subrow.prop(props, "stabilizer_radius", text="Radius", slider=True) + subrow.prop(props, "stabilizer_factor", text="Factor", slider=True) + else: + layout.separator() + layout.prop(props, "use_stabilizer", text="Stabilize Stroke") + col = layout.column(align=False) + col.active = props.use_stabilizer + col.prop(props, "stabilizer_radius", text="Radius", slider=True) + col.prop(props, "stabilizer_factor", text="Factor", slider=True) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) def scribble(*, draw_settings): diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index de78b88c0f6..d85fe16d654 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2256,6 +2256,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): context, ( ({"property": "use_new_hair_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), + ({"property": "use_full_frame_compositor"}, "T88150"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 18c6564b7d4..df520b38eb0 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2314,6 +2314,7 @@ class VIEW3D_MT_object_animation(Menu): layout.operator("nla.bake", text="Bake Action...") layout.operator("gpencil.bake_mesh_animation", text="Bake Mesh to Grease Pencil...") + layout.operator("gpencil.bake_grease_pencil_animation", text="Bake Object Transform to Grease Pencil...") class VIEW3D_MT_object_rigid_body(Menu): @@ -5035,6 +5036,10 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.prop(settings, "use_scale_thickness", text="Scale Thickness") layout.separator() + layout.operator("gpencil.stroke_normalize", text="Normalize Thickness").mode = 'THICKNESS' + layout.operator("gpencil.stroke_normalize", text="Normalize Opacity").mode = 'OPACITY' + + layout.separator() layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform") @@ -6181,6 +6186,9 @@ class VIEW3D_PT_overlay_geometry(Panel): sub.active = overlay.show_fade_inactive sub.prop(overlay, "fade_inactive_alpha", text="Fade Inactive Geometry") + row = col.row(align=True) + row.prop(overlay, "show_mode_transfer", text="Flash on Mode Transfer") + col = layout.column(align=True) col.active = display_all diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 08d581bfa24..604509b99f9 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1456,7 +1456,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): elif brush.gpencil_tool == 'FILL': row = col.row(align=True) row.prop(gp_settings, "fill_draw_mode", text="Boundary") - row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID') + row.prop( + gp_settings, + "show_fill_boundary", + icon='HIDE_OFF' if gp_settings.show_fill_boundary else 'HIDE_ON', + text="", + ) col.separator() row = col.row(align=True) @@ -1465,7 +1470,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): col.separator() row = col.row(align=True) row.prop(gp_settings, "extend_stroke_factor") - row.prop(gp_settings, "show_fill_extend", text="", icon='GRID') + row.prop( + gp_settings, + "show_fill_extend", + icon='HIDE_OFF' if gp_settings.show_fill_extend else 'HIDE_ON', + text="", + ) col.separator() col.prop(gp_settings, "fill_leak", text="Leak Size") diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index bae2c14e3d9..5e245d81de4 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -472,14 +472,6 @@ texture_node_categories = [ ]), ] - -def not_implemented_node(idname): - NodeType = getattr(bpy.types, idname) - name = NodeType.bl_rna.name - label = "%s (mockup)" % name - return NodeItem(idname, label=label) - - geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ @@ -503,6 +495,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ + NodeItem("ShaderNodeRGBCurve"), NodeItem("ShaderNodeValToRGB"), NodeItem("ShaderNodeSeparateRGB"), NodeItem("ShaderNodeCombineRGB"), @@ -510,9 +503,12 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_CURVE", "Curve", items=[ NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), + NodeItem("GeometryNodeMeshToCurve"), + NodeItem("GeometryNodeCurveLength"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), + NodeItem("GeometryNodeDeleteGeometry"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), ]), @@ -565,6 +561,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSwitch"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ + NodeItem("ShaderNodeVectorCurve"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), NodeItem("ShaderNodeVectorMath"), diff --git a/release/steam/README.md b/release/steam/README.md deleted file mode 100644 index 05eda799c3f..00000000000 --- a/release/steam/README.md +++ /dev/null @@ -1,70 +0,0 @@ -Creating Steam builds for Blender -================================= - -This script automates creation of the Steam files: download of the archives, -extraction of the archives, preparation of the build scripts (VDF files), actual -building of the Steam game files. - -Requirements -============ - -* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG - archive did not work Windows nor on Linux using 7-zip. All DMG archives tested - failed to be extracted. As such only MacOS is known to work. -* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the - Steam game files. The path to the `steamcmd` is what is actually needed. -* SteamWorks credentials - Needed to log in using `steamcmd`. -* Login to SteamWorks with the `steamcmd` from the command-line at least once - - Needded to ensure the user is properly logged in. On a new machine the user - will have to go through two-factor authentication. -* App ID and Depot IDs - Needed to create the VDF files. -* Python 3.x - 3.7 was tested. -* Base URL - for downloading the archives. - -Usage -===== - -```bash -$ export STEAMUSER=SteamUserName -$ export STEAMPW=SteamUserPW -$ export BASEURL=https://download.blender.org/release/Blender2.83/ -$ export VERSION=2.83.3 -$ export APPID=appidnr -$ export WINID=winidnr -$ export LINID=linuxidnr -$ export MACOSID=macosidnr - -# log in to SteamWorks from command-line at least once - -$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW - -# once that has been done we can now actually start our tool - -$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd -``` - -All arguments in the above example are required. - -At the start the tool will login using `steamcmd`. This is necessary to let the -Steam SDK update itself if necessary. - -There are a few optional arguments: - -* `--dryrun`: If set building the game files will not actually happen. A set of - log files and a preview manifest per depot will be created in the output folder. - This can be used to double-check everything works as expected. -* `--skipdl`: If set will skip downloading of the archives. The tool expects the - archives to already exist in the correct content location. -* `--skipextract`: If set will skip extraction of all archives. The tool expects - the archives to already have been correctly extracted in the content location. - -Run the tool with `-h` for detailed information on each argument. - -The content and output folders are generated through appending the version -without dots to the words `content` and `output` respectively, e.g. `content2833` -and `output2833`. These folders are created next to the tool. - -From all `.template` files the Steam build scripts will be generated also in the -same directory as the tool. The files will have the extension `.vdf`. - -In case of errors the tool will have a non-zero return code.
\ No newline at end of file diff --git a/release/steam/blender_app_build.vdf.template b/release/steam/blender_app_build.vdf.template deleted file mode 100644 index 9e2d0625d72..00000000000 --- a/release/steam/blender_app_build.vdf.template +++ /dev/null @@ -1,17 +0,0 @@ -"appbuild" -{ - "appid" "[APPID]" - "desc" "Blender [VERSION]" // description for this build - "buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file - "contentroot" "./[CONTENT]" // root content folder, relative to location of this file - "setlive" "" // branch to set live after successful build, non if empty - "preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe - "local" "" // set to flie path of local content server - - "depots" - { - "[WINID]" "depot_build_win.vdf" - "[LINUXID]" "depot_build_linux.vdf" - "[MACOSID]" "depot_build_macos.vdf" - } -} diff --git a/release/steam/create_steam_builds.py b/release/steam/create_steam_builds.py deleted file mode 100644 index 2ecd0c347f7..00000000000 --- a/release/steam/create_steam_builds.py +++ /dev/null @@ -1,397 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import pathlib -import requests -import shutil -import subprocess -from typing import Callable, Iterator, List, Tuple - -# supported archive and platform endings, used to create actual archive names -archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"] - - -def add_optional_argument(option: str, help: str) -> None: - global parser - """Add an optional argument - - Args: - option (str): Option to add - help (str): Help description for the argument - """ - parser.add_argument(option, help=help, action='store_const', const=1) - - -def blender_archives(version: str) -> Iterator[str]: - """Generator for Blender archives for version. - - Yields for items in archive_endings an archive name in the form of - blender-{version}-{ending}. - - Args: - version (str): Version string of the form 2.83.2 - - - Yields: - Iterator[str]: Name in the form of blender-{version}-{ending} - """ - global archive_endings - - for ending in archive_endings: - yield f"blender-{version}-{ending}" - - -def get_archive_type(archive_type: str, version: str) -> str: - """Return the archive of given type and version. - - Args: - archive_type (str): extension for archive type to check for - version (str): Version string in the form 2.83.2 - - Raises: - Exception: Execption when archive type isn't found - - Returns: - str: archive name for given type - """ - - for archive in blender_archives(version): - if archive.endswith(archive_type): - return archive - raise Exception("Unknown archive type") - - -def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str: - """Execute the given command. - - Returns the process stdout upon success if any. - - On error print message the command with name that has failed. Print stdout - and stderr of the process if any, and then exit with given error code. - - Args: - cmd (List[str]): Command in list format, each argument as their own item - name (str): Name of command to use when printing to command-line - errcode (int): Error code to use in case of exit() - cwd (str, optional): Folder to use as current work directory for command - execution. Defaults to ".". - capture_output (bool, optional): Whether to capture command output or not. - Defaults to True. - - Returns: - str: stdout if any, or empty string - """ - cmd_process = subprocess.run( - cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd) - if cmd_process.returncode == 0: - if cmd_process.stdout: - return cmd_process.stdout - else: - return "" - else: - print(f"ERROR: {name} failed.") - if cmd_process.stdout: - print(cmd_process.stdout) - if cmd_process.stderr: - print(cmd_process.stderr) - exit(errcode) - return "" - - -def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path): - """Download archives from the given base_url. - - Archives is a generator for Blender archive names based on version. - - Archive names are appended to the base_url to load from, and appended to - dst_dir to save to. - - Args: - base_url (str): Base URL to load archives from - archives (Callable[[str], Iterator[str]]): Generator for Blender archive - names based on version - version (str): Version string in the form of 2.83.2 - dst_dir (pathlib.Path): Download destination - """ - - if base_url[-1] != '/': - base_url = base_url + '/' - - for archive in archives(version): - download_url = f"{base_url}{archive}" - target_file = dst_dir.joinpath(archive) - download_file(download_url, target_file) - - -def download_file(from_url: str, to_file: pathlib.Path) -> None: - """Download from_url as to_file. - - Actual downloading will be skipped if --skipdl is given on the command-line. - - Args: - from_url (str): Full URL to resource to download - to_file (pathlib.Path): Full path to save downloaded resource as - """ - global args - - if not args.skipdl or not to_file.exists(): - print(f"Downloading {from_url}") - with open(to_file, "wb") as download_zip: - response = requests.get(from_url) - if response.status_code != requests.codes.ok: - print(f"ERROR: failed to download {from_url} (status code: {response.status_code})") - exit(1313) - download_zip.write(response.content) - else: - print(f"Downloading {from_url} skipped") - print(" ... OK") - - -def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None: - """Copy the contents of the given DMG file to the destination folder. - - Args: - dmg_file (pathlib.Path): Full path to DMG archive to extract from - dst (pathlib.Path): Full path to destination to extract to - """ - hdiutil_attach = ["hdiutil", - "attach", - "-readonly", - f"{dmg_file}" - ] - attached = execute_command(hdiutil_attach, "hdiutil attach", 1) - - # Last line of output is what we want, it is of the form - # /dev/somedisk Apple_HFS /Volumes/Blender - # We want to retain the mount point, and the folder the mount is - # created on. The mounted disk we need for detaching, the folder we - # need to be able to copy the contents to where we can use them - attachment_items = attached.splitlines()[-1].split() - mounted_disk = attachment_items[0] - source_location = pathlib.Path(attachment_items[2], "Blender.app") - - print(f"{source_location} -> {dst}") - - shutil.copytree(source_location, dst) - - hdiutil_detach = ["hdiutil", - "detach", - f"{mounted_disk}" - ] - execute_command(hdiutil_detach, "hdiutil detach", 2) - - -def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path: - """ - Create the Steam build script - - Use the given template and template variable tuple list. - - Returns pathlib.Path to the created script. - - Args: - template_name (str): [description] - vars (List[Tuple[str, str]]): [description] - - Returns: - pathlib.Path: Full path to the generated script - """ - build_script = pathlib.Path(".", template_name).read_text() - for var in vars: - build_script = build_script.replace(var[0], var[1]) - build_script_file = template_name.replace(".template", "") - build_script_path = pathlib.Path(".", build_script_file) - build_script_path.write_text(build_script) - return build_script_path - - -def clean_up() -> None: - """Remove intermediate files depending on given command-line arguments - """ - global content_location, args - - if not args.leavearch and not args.leaveextracted: - shutil.rmtree(content_location) - - if args.leavearch and not args.leaveextracted: - shutil.rmtree(content_location.joinpath(zip_extract_folder)) - shutil.rmtree(content_location.joinpath(tarxz_extract_folder)) - shutil.rmtree(content_location.joinpath(dmg_extract_folder)) - - if args.leaveextracted and not args.leavearch: - import os - os.remove(content_location.joinpath(zipped_blender)) - os.remove(content_location.joinpath(tarxz_blender)) - os.remove(content_location.joinpath(dmg_blender)) - - -def extract_archive(archive: str, extract_folder_name: str, - cmd: List[str], errcode: int) -> None: - """Extract all files from archive to given folder name. - - Will not extract if - target folder already exists, or if --skipextract was given on the - command-line. - - Args: - archive (str): Archive name to extract - extract_folder_name (str): Folder name to extract to - cmd (List[str]): Command with arguments to use - errcode (int): Error code to use for exit() - """ - global args, content_location - - extract_location = content_location.joinpath(extract_folder_name) - - pre_extract = set(content_location.glob("*")) - - if not args.skipextract or not extract_location.exists(): - print(f"Extracting files from {archive}...") - cmd.append(content_location.joinpath(archive)) - execute_command(cmd, cmd[0], errcode, cwd=content_location) - # in case we use a non-release archive the naming will be incorrect. - # simply rename to expected target name - post_extract = set(content_location.glob("*")) - diff_extract = post_extract - pre_extract - if not extract_location in diff_extract: - folder_to_rename = list(diff_extract)[0] - folder_to_rename.rename(extract_location) - print(" OK") - else: - print(f"Skipping extraction {archive}!") - -# ============================================================================== - - -parser = argparse.ArgumentParser() - -parser.add_argument("--baseurl", required=True, - help="The base URL for files to download, " - "i.e. https://download.blender.org/release/Blender2.83/") - -parser.add_argument("--version", required=True, - help="The Blender version to release, in the form 2.83.3") - -parser.add_argument("--appid", required=True, - help="The Blender App ID on Steam") -parser.add_argument("--winid", required=True, - help="The Windows depot ID") -parser.add_argument("--linuxid", required=True, - help="The Linux depot ID") -parser.add_argument("--macosid", required=True, - help="The MacOS depot ID") - -parser.add_argument("--steamcmd", required=True, - help="Path to the steamcmd") -parser.add_argument("--steamuser", required=True, - help="The login for the Steam builder user") -parser.add_argument("--steampw", required=True, - help="Login password for the Steam builder user") - -add_optional_argument("--dryrun", - "If set the Steam files will not be uploaded") -add_optional_argument("--leavearch", - help="If set don't clean up the downloaded archives") -add_optional_argument("--leaveextracted", - help="If set don't clean up the extraction folders") -add_optional_argument("--skipdl", - help="If set downloading the archives is skipped if it already exists locally.") -add_optional_argument("--skipextract", - help="If set skips extracting of archives. The tool assumes the archives" - "have already been extracted to their correct locations") - -args = parser.parse_args() - -VERSIONNODOTS = args.version.replace('.', '') -OUTPUT = f"output{VERSIONNODOTS}" -CONTENT = f"content{VERSIONNODOTS}" - -# ===== set up main locations - -content_location = pathlib.Path(".", CONTENT).absolute() -output_location = pathlib.Path(".", OUTPUT).absolute() - -content_location.mkdir(parents=True, exist_ok=True) -output_location.mkdir(parents=True, exist_ok=True) - -# ===== login - -# Logging into Steam once to ensure the SDK updates itself properly. If we don't -# do that the combined +login and +run_app_build_http at the end of the tool -# will fail. -steam_login = [args.steamcmd, - "+login", - args.steamuser, - args.steampw, - "+quit" - ] -print("Logging in to Steam...") -execute_command(steam_login, "Login to Steam", 10) -print(" OK") - -# ===== prepare Steam build scripts - -template_vars = [ - ("[APPID]", args.appid), - ("[OUTPUT]", OUTPUT), - ("[CONTENT]", CONTENT), - ("[VERSION]", args.version), - ("[WINID]", args.winid), - ("[LINUXID]", args.linuxid), - ("[MACOSID]", args.macosid), - ("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0") -] - -blender_app_build = create_build_script( - "blender_app_build.vdf.template", template_vars) -create_build_script("depot_build_win.vdf.template", template_vars) -create_build_script("depot_build_linux.vdf.template", template_vars) -create_build_script("depot_build_macos.vdf.template", template_vars) - -# ===== download archives - -download_archives(args.baseurl, blender_archives, - args.version, content_location) - -# ===== set up file and folder names - -zipped_blender = get_archive_type("zip", args.version) -zip_extract_folder = zipped_blender.replace(".zip", "") -tarxz_blender = get_archive_type("tar.xz", args.version) -tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "") -dmg_blender = get_archive_type("dmg", args.version) -dmg_extract_folder = dmg_blender.replace(".dmg", "") - -# ===== extract - -unzip_cmd = ["unzip", "-q"] -extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3) - -untarxz_cmd = ["tar", "-xf"] -extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4) - -if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists(): - print("Extracting files from Blender MacOS archive...") - blender_dmg = content_location.joinpath(dmg_blender) - target_location = content_location.joinpath( - dmg_extract_folder, "Blender.app") - copy_contents_from_dmg_to_path(blender_dmg, target_location) - print(" OK") -else: - print("Skipping extraction of .dmg!") - -# ===== building - -print("Build Steam game files...") -steam_build = [args.steamcmd, - "+login", - args.steamuser, - args.steampw, - "+run_app_build_http", - blender_app_build.absolute(), - "+quit" - ] -execute_command(steam_build, "Build with steamcmd", 13) -print(" OK") - -clean_up() diff --git a/release/steam/depot_build_linux.vdf.template b/release/steam/depot_build_linux.vdf.template deleted file mode 100644 index 0f69008548e..00000000000 --- a/release/steam/depot_build_linux.vdf.template +++ /dev/null @@ -1,31 +0,0 @@ -"DepotBuildConfig" -{ - // Set your assigned depot ID here - "DepotID" "[LINUXID]" - - // Set a root for all content. - // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) - // will be resolved relative to this root. - // If you don't define ContentRoot, then it will be assumed to be - // the location of this script file, which probably isn't what you want - "ContentRoot" "./blender-[VERSION]-linux64/" - - // include all files recursivley - "FileMapping" - { - // This can be a full path, or a path relative to ContentRoot - "LocalPath" "*" - - // This is a path relative to the install folder of your game - "DepotPath" "." - - // If LocalPath contains wildcards, setting this means that all - // matching files within subdirectories of LocalPath will also - // be included. - "recursive" "1" - } - - // but exclude all symbol files - // This can be a full path, or a path relative to ContentRoot - "FileExclusion" "*.pdb" -} diff --git a/release/steam/depot_build_macos.vdf.template b/release/steam/depot_build_macos.vdf.template deleted file mode 100644 index 33dde860462..00000000000 --- a/release/steam/depot_build_macos.vdf.template +++ /dev/null @@ -1,30 +0,0 @@ -"DepotBuildConfig" -{ - // Set your assigned depot ID here - "DepotID" "[MACOSID]" - - // Set a root for all content. - // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) - // will be resolved relative to this root. - // If you don't define ContentRoot, then it will be assumed to be - // the location of this script file, which probably isn't what you want - "ContentRoot" "./blender-[VERSION]-macOS/" - // include all files recursivley - "FileMapping" - { - // This can be a full path, or a path relative to ContentRoot - "LocalPath" "*" - - // This is a path relative to the install folder of your game - "DepotPath" "." - - // If LocalPath contains wildcards, setting this means that all - // matching files within subdirectories of LocalPath will also - // be included. - "recursive" "1" - } - - // but exclude all symbol files - // This can be a full path, or a path relative to ContentRoot - "FileExclusion" "*.pdb" -} diff --git a/release/steam/depot_build_win.vdf.template b/release/steam/depot_build_win.vdf.template deleted file mode 100644 index 2c18a0f15dd..00000000000 --- a/release/steam/depot_build_win.vdf.template +++ /dev/null @@ -1,31 +0,0 @@ -"DepotBuildConfig" -{ - // Set your assigned depot ID here - "DepotID" "[WINID]" - - // Set a root for all content. - // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) - // will be resolved relative to this root. - // If you don't define ContentRoot, then it will be assumed to be - // the location of this script file, which probably isn't what you want - "ContentRoot" "./blender-[VERSION]-windows64/" - - // include all files recursivley - "FileMapping" - { - // This can be a full path, or a path relative to ContentRoot - "LocalPath" "*" - - // This is a path relative to the install folder of your game - "DepotPath" "." - - // If LocalPath contains wildcards, setting this means that all - // matching files within subdirectories of LocalPath will also - // be included. - "recursive" "1" - } - - // but exclude all symbol files - // This can be a full path, or a path relative to ContentRoot - "FileExclusion" "*.pdb" -} diff --git a/release/windows/manifest/blender.exe.manifest.in b/release/windows/manifest/blender.exe.manifest.in index e73ddf3267b..b516efe24cb 100644 --- a/release/windows/manifest/blender.exe.manifest.in +++ b/release/windows/manifest/blender.exe.manifest.in @@ -13,12 +13,6 @@ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- Windows 8.1 --> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> - <!-- Windows 8 --> - <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> - <!-- Windows 7 --> - <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> - <!-- Windows Vista --> - <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> </application> </compatibility> <dependency> diff --git a/release/windows/msix/README.md b/release/windows/msix/README.md index 3f661a44066..96f753f0e78 100644 --- a/release/windows/msix/README.md +++ b/release/windows/msix/README.md @@ -1,82 +1,4 @@ -create_msix_package -=================== +Buildbot Configuration +====================== -This tool is used to create MSIX packages from a given ZiP archive. The MSIX -package is distributed mainly through the Microsoft Store. It can also be -installed when downloaded from blender.org. For that to work the MSIX package -needs to be signed. - -Requirements -============ - -* MakeAppX.exe - this tool is distributed with the Windows 10 SDK and is used to build the .appx package. -* MakePri.exe - this tool is distributed with the Windows 10 SDK and is used to generate a resources file. -* SignTool.exe - this tool is distributed with the Windows 10 SDK and is used to sign the .appx package. -* Python 3 (3.7 or later tested) - to run the create_msix_package.py script -* requests module - can be installed with `pip install requests` -* PFX file (optional, but strongly recommended) - for signing the resulting MSIX - package. **NOTE:** If the MSIX package is not signed when uploaded to the Microsoft - store the validation and certification process can take up to three full - business day. - -Usage -===== - -On the command-line: -```batch -set VERSION=2.83.4.0 -set URL=https://download.blender.org/release/Blender2.83/blender-2.83.4-windows64.zip -set PUBID=CN=PUBIDHERE -set PFX=X:\path\to\cert.pfx -set PFXPW=pwhere - -python create_msix_package.py --version %VERSION% --url %URL% --publisher %PUBID% --pfx %PFX% --password %PFXPW% -``` - -Result will be a MSIX package with the name `blender-2.83.4-windows64.msix`. -With the above usage it will be signed. If the signing options are left out the -package will not be signed. - -Optional arguments -================== - -In support of testing and developing the manifest and scripts there are a few -optional arguments: - -* `--skipdl` : If a `blender.zip` is available already next to the tool use this - to skip actual downloading of the archive designated by `--url`. The latter - option is still required -* `--overwrite` : When script fails the final clean-up may be incomplete leaving - the `Content` folder with its structure. Specify this argument to automatically - clean up this folder before starting to seed the `Content` folder -* `--leavezip` : When specified leave the `blender.zip` file while cleaning up - all other intermediate files, including the `Content` folder. This is useful - to not have to re-download the same archive from `--url` on each usage - - -What it does -============ - -The tool creates in the directory it lives a subfolder called `Content`. This is -where all necessary files are placed. - -The `Assets` folder is copied to the `Content` folder. - -From the application manifest template a version with necessary parts replaced as -their actual values as specified on the command-line is realized. This manifest controls the packaging of Blender into the MSIX format. - -Next the tool downloads the designated ZIP archive locally as blender.zip. From -this archive the files are extracted into the `Content\Blender` folder, but skip -the leading part of paths in the ZIP. We want to write the files to the -content_blender_folder where blender.exe ends up as -`Content\Blender\blender.exe`, and not -`Content\Blender\blender-2.83.4-windows64\blender.exe` - -Once the extraction is completed the MakeAppX tool is executed with the `Content` -folder as input. The result will be the MSIX package with the name in the form -`blender-X.YY.Z-windows64.msix`. - -If the PFX file and its password are given on the command-line this MSIX package -will be signed. - -All intermediate files and directories will be removed. +Files used by Buildbot's `package-code-store-windows` step. diff --git a/release/windows/msix/create_msix_package.py b/release/windows/msix/create_msix_package.py deleted file mode 100644 index 3e41484eef5..00000000000 --- a/release/windows/msix/create_msix_package.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import pathlib -import requests -import shutil -import subprocess -import zipfile - -parser = argparse.ArgumentParser() -parser.add_argument( - "--version", - required=True, - help="Version string in the form of 2.83.3.0", -) -parser.add_argument( - "--url", - required=True, - help="Location of the release ZIP archive to download", -) -parser.add_argument( - "--publisher", - required=True, - help="A string in the form of 'CN=PUBLISHER'", -) -parser.add_argument( - "--pfx", - required=False, - help="Absolute path to the PFX file used for signing the resulting MSIX package", -) -parser.add_argument( - "--password", - required=False, - default="blender", - help="Password for the PFX file", -) -parser.add_argument( - "--lts", - required=False, - help="If set this MSIX is for an LTS release", - action='store_const', - const=1, -) -parser.add_argument( - "--skipdl", - required=False, - help="If set skip downloading of the specified URL as blender.zip. The tool assumes blender.zip exists", - action='store_const', - const=1, -) -parser.add_argument( - "--leavezip", - required=False, - help="If set don't clean up the downloaded blender.zip", - action='store_const', - const=1, -) -parser.add_argument( - "--overwrite", - required=False, - help="If set remove Content folder if it already exists", - action='store_const', - const=1, -) -args = parser.parse_args() - - -def execute_command(cmd: list, name: str, errcode: int): - """ - Execute given command in cmd. Output is captured. If an error - occurs name is used to print ERROR message, along with stderr and - stdout of the process if either was captured. - """ - cmd_process = subprocess.run(cmd, capture_output=True, encoding="UTF-8") - if cmd_process.returncode != 0: - print(f"ERROR: {name} failed.") - if cmd_process.stdout: - print(cmd_process.stdout) - if cmd_process.stderr: - print(cmd_process.stderr) - exit(errcode) - - -LTSORNOT = "" -PACKAGETYPE = "" -if args.lts: - versionparts = args.version.split(".") - LTSORNOT = f" {versionparts[0]}.{versionparts[1]} LTS" - PACKAGETYPE = f"{versionparts[0]}.{versionparts[1]}LTS" - -blender_package_msix = pathlib.Path(".", f"blender-{args.version}-windows64.msix").absolute() -content_folder = pathlib.Path(".", "Content") -content_blender_folder = pathlib.Path(content_folder, "Blender").absolute() -content_assets_folder = pathlib.Path(content_folder, "Assets") -assets_original_folder = pathlib.Path(".", "Assets") - -pri_config_file = pathlib.Path(".", "priconfig.xml") -pri_resources_file = pathlib.Path(content_folder, "resources.pri") - -local_blender_zip = pathlib.Path(".", "blender.zip") - -if args.pfx: - pfx_path = pathlib.Path(args.pfx) - if not pfx_path.exists(): - print("ERROR: PFX file not found. Please ensure you give the correct path to the PFX file on the command-line.") - exit(1) - print(f"Creating MSIX package with signing using PFX file at {pfx_path}") -else: - pfx_path = None - print("Creating MSIX package without signing.") - -pri_command = ["makepri", - "new", - "/pr", f"{content_folder.absolute()}", - "/cf", f"{pri_config_file.absolute()}", - "/of", f"{pri_resources_file.absolute()}" - ] - -msix_command = ["makeappx", - "pack", - "/h", "SHA256", - "/d", f"{content_folder.absolute()}", - "/p", f"{blender_package_msix}" - ] -if pfx_path: - sign_command = ["signtool", - "sign", - "/fd", "sha256", - "/a", "/f", f"{pfx_path.absolute()}", - "/p", f"{args.password}", - f"{blender_package_msix}" - ] - -if args.overwrite: - if content_folder.joinpath("Assets").exists(): - shutil.rmtree(content_folder) -content_folder.mkdir(exist_ok=True) -shutil.copytree(assets_original_folder, content_assets_folder) - -manifest_text = pathlib.Path("AppxManifest.xml.template").read_text() -manifest_text = manifest_text.replace("[VERSION]", args.version) -manifest_text = manifest_text.replace("[PUBLISHER]", args.publisher) -manifest_text = manifest_text.replace("[LTSORNOT]", LTSORNOT) -manifest_text = manifest_text.replace("[PACKAGETYPE]", PACKAGETYPE) -pathlib.Path(content_folder, "AppxManifest.xml").write_text(manifest_text) - -if not args.skipdl: - print(f"Downloading blender archive {args.url} to {local_blender_zip}...") - - with open(local_blender_zip, "wb") as download_zip: - response = requests.get(args.url) - download_zip.write(response.content) - - print("... download complete.") -else: - print("Skipping download") - -print(f"Extracting files from ZIP to {content_blender_folder}...") - -# Extract the files from the ZIP archive, but skip the leading part of paths -# in the ZIP. We want to write the files to the content_blender_folder where -# blender.exe ends up as ./Content/Blender/blender.exe, and not -# ./Content/Blender/blender-2.83.3-windows64/blender.exe -with zipfile.ZipFile(local_blender_zip, "r") as blender_zip: - for entry in blender_zip.infolist(): - if entry.is_dir(): - continue - entry_location = pathlib.Path(entry.filename) - target_location = content_blender_folder.joinpath(*entry_location.parts[1:]) - pathlib.Path(target_location.parent).mkdir(parents=True, exist_ok=True) - extracted_entry = blender_zip.read(entry) - target_location.write_bytes(extracted_entry) - -print("... extraction complete.") - - -print(f"Generating Package Resource Index (PRI) file using command: {' '.join(pri_command)}") -execute_command(pri_command, "MakePri", 4) - -print(f"Creating MSIX package using command: {' '.join(msix_command)}") - -# Remove MSIX file if it already exists. Otherwise the MakeAppX tool -# will hang. -if blender_package_msix.exists(): - os.remove(blender_package_msix) -execute_command(msix_command, "MakeAppX", 2) - -if args.pfx: - print(f"Signing MSIX package using command: {' '.join(sign_command)}") - execute_command(sign_command, "SignTool", 3) - -if not args.leavezip: - os.remove(local_blender_zip) -shutil.rmtree(content_folder) - -print("Done.") diff --git a/source/blender/blenfont/intern/blf_font_win32_compat.c b/source/blender/blenfont/intern/blf_font_win32_compat.c index 7d130204c07..e573d3bd224 100644 --- a/source/blender/blenfont/intern/blf_font_win32_compat.c +++ b/source/blender/blenfont/intern/blf_font_win32_compat.c @@ -66,7 +66,7 @@ static unsigned long ft_ansi_stream_io(FT_Stream stream, file = STREAM_FILE(stream); if (stream->pos != offset) { - fseek(file, offset, SEEK_SET); + BLI_fseek(file, offset, SEEK_SET); } return fread(buffer, 1, count, file); @@ -93,7 +93,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Resource); } - fseek(file, 0, SEEK_END); + BLI_fseek(file, 0LL, SEEK_END); stream->size = ftell(file); if (!stream->size) { fprintf(stderr, @@ -104,7 +104,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Stream); } - fseek(file, 0, SEEK_SET); + BLI_fseek(file, 0LL, SEEK_SET); stream->descriptor.pointer = file; stream->read = ft_ansi_stream_io; diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c3bc4d3ca4a..381a03d29d7 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -333,6 +333,21 @@ class CustomDataAttributes { void reallocate(const int size); std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const; + + blender::fn::GVArrayPtr get_for_read(const StringRef name, + const CustomDataType data_type, + const void *default_value) const; + + template<typename T> + blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name, + const T &default_value) const + { + const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + GVArrayPtr varray = this->get_for_read(name, type, &default_value); + return blender::fn::GVArray_Typed<T>(std::move(varray)); + } + std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name); bool create(const blender::StringRef name, const CustomDataType data_type); bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer); diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 0afdc436415..ba683362e69 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -52,7 +52,7 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f func(bool()); break; case CD_PROP_COLOR: - func(Color4f()); + func(ColorGeometry4f()); break; default: BLI_assert_unreachable(); @@ -78,8 +78,8 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func else if (cpp_type.is<bool>()) { func(bool()); } - else if (cpp_type.is<Color4f>()) { - func(Color4f()); + else if (cpp_type.is<ColorGeometry4f>()) { + func(ColorGeometry4f()); } else { BLI_assert_unreachable(); @@ -123,9 +123,12 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co } template<> -inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2) +inline ColorGeometry4f mix3(const float3 &weights, + const ColorGeometry4f &v0, + const ColorGeometry4f &v1, + const ColorGeometry4f &v2) { - Color4f result; + ColorGeometry4f result; interp_v4_v4v4v4(result, v0, v1, v2, weights); return result; } @@ -165,9 +168,10 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3 return float3::interpolate(a, b, factor); } -template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b) +template<> +inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) { - Color4f result; + ColorGeometry4f result; interp_v4_v4v4(result, a, b, factor); return result; } @@ -274,15 +278,16 @@ class SimpleMixerWithAccumulationType { } }; -class Color4fMixer { +class ColorGeometryMixer { private: - MutableSpan<Color4f> buffer_; - Color4f default_color_; + MutableSpan<ColorGeometry4f> buffer_; + ColorGeometry4f default_color_; Array<float> total_weights_; public: - Color4fMixer(MutableSpan<Color4f> buffer, Color4f default_color = {0, 0, 0, 1}); - void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f); + ColorGeometryMixer(MutableSpan<ColorGeometry4f> buffer, + ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f); void finalize(); }; @@ -299,10 +304,10 @@ template<> struct DefaultMixerStruct<float2> { template<> struct DefaultMixerStruct<float3> { using type = SimpleMixer<float3>; }; -template<> struct DefaultMixerStruct<Color4f> { - /* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not +template<> struct DefaultMixerStruct<ColorGeometry4f> { + /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not * something one should usually do with colors. */ - using type = Color4fMixer; + using type = ColorGeometryMixer; }; template<> struct DefaultMixerStruct<int> { static int double_to_int(const double &value) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 0bab980cfcd..6ad910ff8ab 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 1 +#define BLENDER_FILE_SUBVERSION 3 /* 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 diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index f04b5e45720..ef2a0ed34a0 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -30,11 +30,60 @@ struct Main; struct PointerRNA; /** - * Common suffix uses: - * - ``_PRE/_POST``: - * For handling discrete non-interactive events. - * - ``_INIT/_COMPLETE/_CANCEL``: - * For handling jobs (which may in turn cause other handlers to be called). + Callbacks for One Off Actions + * ============================= + * + * - `{ACTION}` use in cases where only a single callback is required, + * `VERSION_UPDATE` and `RENDER_STATS` for example. + * + * \note avoid single callbacks if there is a chance `PRE/POST` are useful to differentiate + * since renaming callbacks may break Python scripts. + * + * Callbacks for Common Actions + * ============================ + * + * - `{ACTION}_PRE` run before the action. + * - `{ACTION}_POST` run after the action. + * + * Optional Additional Callbacks + * ----------------------------- + * + * - `{ACTION}_INIT` when the handler may manipulate the context used to run the action. + * + * Examples where `INIT` functions may be useful are: + * + * - When rendering, an `INIT` function may change the camera or render settings, + * things which a `PRE` function can't support as this information has already been used. + * - When saving an `INIT` function could temporarily change the preferences. + * + * - `{ACTION}_POST_FAIL` should be included if the action may fail. + * + * Use this so a call to the `PRE` callback always has a matching call to `POST` or `POST_FAIL`. + * + * \note in most cases only `PRE/POST` are required. + * + * Callbacks for Background/Modal Tasks + * ==================================== + * + * - `{ACTION}_INIT` + * - `{ACTION}_COMPLETE` when a background job has finished. + * - `{ACTION}_CANCEL` When a background job is canceled partway through. + * + * While cancellation may be caused by any number of reasons, common causes may include: + * + * - Explicit user cancellation. + * - Exiting Blender. + * - Failure to acquire resources (such as disk-full, out of memory ... etc). + * + * \note `PRE/POST` handlers may be used along side modal task handlers + * as is the case for rendering, where rendering an animation uses modal task handlers, + * rendering a single frame has `PRE/POST` handlers. + * + * Python Access + * ============= + * + * All callbacks here must be exposed via the Python module `bpy.app.handlers`, + * see `bpy_app_handlers.c`. */ typedef enum { BKE_CB_EVT_FRAME_CHANGE_PRE, diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 2fb713a4299..3a1eedfd807 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -33,6 +33,7 @@ extern "C" { struct BMLoop; struct BMesh; +struct BMPartialUpdate; struct BoundBox; struct Depsgraph; struct Mesh; @@ -85,6 +86,8 @@ typedef struct BMEditMesh { /* editmesh.c */ void BKE_editmesh_looptri_calc(BMEditMesh *em); +void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo); + BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate); BMEditMesh *BKE_editmesh_copy(BMEditMesh *em); BMEditMesh *BKE_editmesh_from_object(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3b3856f11ab..b2342a5fd96 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -39,7 +39,8 @@ struct Mesh; struct Object; struct PointCloud; struct Volume; -class CurveEval; +struct Curve; +struct CurveEval; enum class GeometryOwnershipType { /* The geometry is owned. This implies that it can be changed. */ @@ -406,6 +407,15 @@ class CurveComponent : public GeometryComponent { CurveEval *curve_ = 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 + * we use #CurveEval rather than #Curve here. It also allows us to mostly reuse the same + * batch cache implementation. + */ + mutable Curve *curve_for_render_ = nullptr; + mutable std::mutex curve_for_render_mutex_; + public: CurveComponent(); ~CurveComponent(); @@ -420,12 +430,18 @@ class CurveComponent : public GeometryComponent { CurveEval *get_for_write(); int attribute_domain_size(const AttributeDomain domain) const final; + std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const final; bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; + const Curve *get_curve_for_render() const; + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; private: diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 6b706f3bcd0..e16507bf3cc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -258,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); -bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name) - ATTR_NONNULL(1, 2); +bool BKE_id_new_name_validate(struct ListBase *lb, + struct ID *id, + const char *name, + const bool do_linked_data) ATTR_NONNULL(1, 2); void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); /* Affect whole Main database. */ @@ -273,7 +275,7 @@ void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value); void BKE_main_id_flag_listbase(struct ListBase *lb, const int flag, const bool value); void BKE_main_id_flag_all(struct Main *bmain, const int flag, const bool value); -void BKE_main_id_clear_newpoins(struct Main *bmain); +void BKE_main_id_newptr_and_tag_clear(struct Main *bmain); void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_only); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index f1ed5a453ba..4dc99e64cf2 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -47,6 +47,7 @@ struct ID; struct IDOverrideLibrary; struct IDOverrideLibraryProperty; struct IDOverrideLibraryPropertyOperation; +struct Library; struct Main; struct Object; struct PointerRNA; @@ -68,12 +69,15 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id); struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, struct ID *reference_id, const bool do_tagged_remap); -bool BKE_lib_override_library_create_from_tag(struct Main *bmain); +bool BKE_lib_override_library_create_from_tag(struct Main *bmain, + const struct Library *reference_library, + const bool do_no_main); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct ID *id_root, - struct ID *id_reference); + struct ID *id_reference, + struct ID **r_id_root_override); bool BKE_lib_override_library_template_create(struct ID *id); bool BKE_lib_override_library_proxy_convert(struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 705d2b030e5..e806dedc14c 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -85,6 +85,10 @@ enum { * freed ones). */ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7, + /** Force handling user count even for IDs that are outside of Main (used in some cases when + * dealing with IDs temporarily out of Main, but which will be put in it ultimately). + */ + ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8, }; /* Note: Requiring new_id to be non-null, this *may* not be the case ultimately, diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index dc471fcb62f..69e2d52e1dd 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -113,6 +113,7 @@ void BKE_id_material_clear(struct Main *bmain, struct ID *id); struct Material *BKE_object_material_get_eval(struct Object *ob, short act); int BKE_object_material_count_eval(struct Object *ob); void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material); +void BKE_id_material_eval_ensure_default_slot(struct ID *id); /* rendering */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 6bebf74824d..380e75f0924 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -325,7 +325,7 @@ typedef struct bNodeType { /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; - bool geometry_node_execute_supports_lazyness; + bool geometry_node_execute_supports_laziness; /* RNA integration */ ExtensionRNA rna_ext; @@ -1305,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteSyncFromAdd(bNode *node); +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); void ntreeCompositCryptomatteSyncFromRemove(bNode *node); bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); /* Update the runtime layer names with the cryptomatte layer names of the references * render layer or image. */ -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node); +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); /** \} */ @@ -1426,6 +1429,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_MESH_TO_CURVE 1052 +#define GEO_NODE_DELETE_GEOMETRY 1053 +#define GEO_NODE_CURVE_LENGTH 1054 /** \} */ diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh index 8bf89cd8f58..4ec165aad8c 100644 --- a/source/blender/blenkernel/BKE_node_ui_storage.hh +++ b/source/blender/blenkernel/BKE_node_ui_storage.hh @@ -95,21 +95,13 @@ struct AvailableAttributeInfo { }; struct NodeUIStorage { - std::mutex mutex; blender::Vector<NodeWarning> warnings; blender::Set<AvailableAttributeInfo> attribute_hints; - - NodeUIStorage() = default; - /* Needed because the mutex can't be moved or copied. */ - NodeUIStorage(NodeUIStorage &&other) - : warnings(std::move(other.warnings)), attribute_hints(std::move(other.attribute_hints)) - { - } }; struct NodeTreeUIStorage { + std::mutex mutex; blender::Map<NodeTreeEvaluationContext, blender::Map<std::string, NodeUIStorage>> context_map; - std::mutex context_map_mutex; /** * Attribute search uses this to store the fake info for the string typed into a node, in order diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 9e5552082af..fc1292e3620 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -101,15 +101,14 @@ class Spline { Spline(const Type type) : type_(type) { } - Spline(Spline &other) - : normal_mode(other.normal_mode), - attributes(other.attributes), - type_(other.type_), - is_cyclic_(other.is_cyclic_) + Spline(Spline &other) : attributes(other.attributes), type_(other.type_) { + copy_base_settings(other, *this); } virtual SplinePtr copy() const = 0; + /** Return a new spline with the same type and settings like "cyclic", but without any data. */ + virtual SplinePtr copy_settings() const = 0; Spline::Type type() const; @@ -183,6 +182,12 @@ class Spline { protected: virtual void correct_end_tangents() const = 0; + /** Copy settings stored in the base spline class. */ + static void copy_base_settings(const Spline &src, Spline &dst) + { + dst.normal_mode = src.normal_mode; + dst.is_cyclic_ = src.is_cyclic_; + } }; /** @@ -236,6 +241,7 @@ class BezierSpline final : public Spline { public: virtual SplinePtr copy() const final; + SplinePtr copy_settings() const final; BezierSpline() : Spline(Type::Bezier) { } @@ -257,10 +263,10 @@ class BezierSpline final : public Spline { void set_resolution(const int value); void add_point(const blender::float3 position, - const HandleType handle_type_start, - const blender::float3 handle_position_start, - const HandleType handle_type_end, - const blender::float3 handle_position_end, + const HandleType handle_type_left, + const blender::float3 handle_position_left, + const HandleType handle_type_right, + const blender::float3 handle_position_right, const float radius, const float tilt); @@ -377,6 +383,7 @@ class NURBSpline final : public Spline { public: SplinePtr copy() const final; + SplinePtr copy_settings() const final; NURBSpline() : Spline(Type::NURBS) { } @@ -444,6 +451,7 @@ class PolySpline final : public Spline { public: SplinePtr copy() const final; + SplinePtr copy_settings() const final; PolySpline() : Spline(Type::Poly) { } @@ -483,7 +491,7 @@ class PolySpline final : public Spline { * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since * more of the data is stored in the splines, but also just to be different than the name in DNA. */ -class CurveEval { +struct CurveEval { private: blender::Vector<SplinePtr> splines_; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 62833e10438..f2ad873b10e 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -46,6 +46,8 @@ using blender::StringRef; using blender::StringRefNull; using blender::fn::GMutableSpan; using blender::fn::GSpan; +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_SingleValue; namespace blender::bke { @@ -61,7 +63,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty case CD_PROP_INT32: return &CPPType::get<int>(); case CD_PROP_COLOR: - return &CPPType::get<Color4f>(); + return &CPPType::get<ColorGeometry4f>(); case CD_PROP_BOOL: return &CPPType::get<bool>(); default: @@ -84,7 +86,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is<int>()) { return CD_PROP_INT32; } - if (type.is<Color4f>()) { + if (type.is<ColorGeometry4f>()) { return CD_PROP_COLOR; } if (type.is<bool>()) { @@ -355,7 +357,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( case CD_PROP_INT32: return this->layer_to_read_attribute<int>(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_read_attribute<Color4f>(layer, domain_size); + return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_read_attribute<bool>(layer, domain_size); default: @@ -389,7 +391,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( case CD_PROP_INT32: return this->layer_to_write_attribute<int>(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_write_attribute<Color4f>(layer, domain_size); + return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_write_attribute<bool>(layer, domain_size); default: @@ -628,8 +630,35 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) co return {}; } +/** + * Return a virtual array for a stored attribute, or a single value virtual array with the default + * value if the attribute doesn't exist. If no default value is provided, the default value for the + * type will be used. + */ +GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, + const CustomDataType data_type, + const void *default_value) const +{ + const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + + std::optional<GSpan> attribute = this->get_for_read(name); + if (!attribute) { + const int domain_size = this->size_; + return std::make_unique<GVArray_For_SingleValue>( + *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value); + } + + if (attribute->type() == *type) { + return std::make_unique<GVArray_For_GSpan>(*attribute); + } + const blender::nodes::DataTypeConversions &conversions = + blender::nodes::get_implicit_type_conversions(); + return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type); +} + std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name) { + /* If this assert hits, it most likely means that #reallocate was not called at some point. */ BLI_assert(size_ != 0); for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) { if (layer.name == name) { diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index 4ff3a6ceff5..5cdf329effb 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -18,18 +18,21 @@ namespace blender::attribute_math { -Color4fMixer::Color4fMixer(MutableSpan<Color4f> output_buffer, Color4f default_color) +ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffer, + ColorGeometry4f default_color) : buffer_(output_buffer), default_color_(default_color), total_weights_(output_buffer.size(), 0.0f) { - buffer_.fill(Color4f(0, 0, 0, 0)); + buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); } -void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight) +void ColorGeometryMixer::mix_in(const int64_t index, + const ColorGeometry4f &color, + const float weight) { BLI_assert(weight >= 0.0f); - Color4f &output_color = buffer_[index]; + ColorGeometry4f &output_color = buffer_[index]; output_color.r += color.r * weight; output_color.g += color.g * weight; output_color.b += color.b * weight; @@ -37,11 +40,11 @@ void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float total_weights_[index] += weight; } -void Color4fMixer::finalize() +void ColorGeometryMixer::finalize() { for (const int64_t i : buffer_.index_range()) { const float weight = total_weights_[i]; - Color4f &output_color = buffer_[i]; + ColorGeometry4f &output_color = buffer_[i]; if (weight > 0.0f) { const float weight_inv = 1.0f / weight; output_color.r *= weight_inv; diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 3170c3aa65c..7b1aaf04640 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -694,8 +694,7 @@ Collection *BKE_collection_duplicate(Main *bmain, const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ if (ID_IS_LINKED(collection)) { @@ -726,8 +725,7 @@ Collection *BKE_collection_duplicate(Main *bmain, #endif /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_collection_sync(bmain); } @@ -1425,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } @@ -1644,6 +1643,13 @@ void BKE_collection_parent_relations_rebuild(Collection *collection) continue; } + /* Can happen when remapping data partially out-of-Main (during advanced ID management + * operations like liboverride resync e.g.). */ + if ((child->collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) { + continue; + } + + BLI_assert(collection_find_parent(child->collection, collection) == NULL); CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), __func__); cparent->collection = collection; BLI_addtail(&child->collection->parents, cparent); @@ -1652,10 +1658,19 @@ void BKE_collection_parent_relations_rebuild(Collection *collection) static void collection_parents_rebuild_recursive(Collection *collection) { + /* A same collection may be child of several others, no need to process it more than once. */ + if ((collection->tag & COLLECTION_TAG_RELATION_REBUILD) == 0) { + return; + } + BKE_collection_parent_relations_rebuild(collection); collection->tag &= ~COLLECTION_TAG_RELATION_REBUILD; for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) { + /* See comment above in `BKE_collection_parent_relations_rebuild`. */ + if ((collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) { + continue; + } collection_parents_rebuild_recursive(child->collection); } } @@ -1679,6 +1694,8 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain) /* This function can be called from readfile.c, when this pointer is not guaranteed to be NULL. */ if (scene->master_collection != NULL) { + BLI_assert(BLI_listbase_is_empty(&scene->master_collection->parents)); + scene->master_collection->tag |= COLLECTION_TAG_RELATION_REBUILD; collection_parents_rebuild_recursive(scene->master_collection); } } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 9293a2b449a..826c79c3764 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1635,10 +1635,28 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN float eul[3]; float size[3]; + /* This constraint is based on euler rotation math, which doesn't work well with shear. + * The Y axis is chosen as the main one because constraints are most commonly used on bones. + * This also allows using the constraint to simply remove shear. */ + orthogonalize_m4_stable(cob->matrix, 1, false); + + /* Only do the complex processing if some limits are actually enabled. */ + if (!(data->flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT))) { + return; + } + + /* Select the Euler rotation order, defaulting to the owner value. */ + short rot_order = cob->rotOrder; + + if (data->euler_order != CONSTRAINT_EULER_AUTO) { + rot_order = data->euler_order; + } + + /* Decompose the matrix using the specified order. */ copy_v3_v3(loc, cob->matrix[3]); mat4_to_size(size, cob->matrix); - mat4_to_eulO(eul, cob->rotOrder, cob->matrix); + mat4_to_eulO(eul, rot_order, cob->matrix); /* constraint data uses radians internally */ @@ -1671,7 +1689,7 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN } } - loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder); + loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order); } static bConstraintTypeInfo CTI_ROTLIMIT = { diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 470c2f2d246..4aaecc26eff 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += header->structbytes; header->structbytes = sizeof(CDataFileHeader); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf) mesh->structbytes = sizeof(CDataFileMeshHeader); } - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += layer->structbytes; layer->structbytes = sizeof(CDataFileLayer); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } } @@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) offset += cdf->layer[a].datasize; } - return (fseek(cdf->readf, offset, SEEK_SET) == 0); + return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0); } bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 0fa3bb29ccd..472de1f3c77 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) } em->looptris = looptris; + em->tottri = looptris_tot; /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri); + BM_mesh_calc_tessellation(em->bm, em->looptris); } void BKE_editmesh_looptri_calc(BMEditMesh *em) @@ -148,6 +149,14 @@ void BKE_editmesh_looptri_calc(BMEditMesh *em) #endif } +void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo) +{ + BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop)); + BLI_assert(em->looptris != NULL); + + BM_mesh_calc_tessellation_with_partial(em->bm, em->looptris, bmpinfo); +} + void BKE_editmesh_free_derivedmesh(BMEditMesh *em) { if (em->mesh_eval_cage) { diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 30d5a54b479..68ed3c239ef 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1559,8 +1559,7 @@ void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], con } /** - . - * Find roots of cubic equation (c0 x³ + c1 x² + c2 x + c3) + * Find roots of cubic equation (c0 x^3 + c1 x^2 + c2 x + c3) * \return number of roots in `o`. * * \note it is up to the caller to allocate enough memory for `o`. diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 851d8aae378..493a267c2f0 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -623,7 +623,8 @@ static void clamp_bounds_in_domain(FluidDomainSettings *fds, static bool is_static_object(Object *ob) { /* Check if the object has modifiers that might make the object "dynamic". */ - ModifierData *md = ob->modifiers.first; + VirtualModifierData virtualModifierData; + ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); for (; md; md = md->next) { if (ELEM(md->type, eModifierType_Cloth, @@ -631,7 +632,8 @@ static bool is_static_object(Object *ob) eModifierType_Explode, eModifierType_Ocean, eModifierType_ShapeKey, - eModifierType_Softbody)) { + eModifierType_Softbody, + eModifierType_Nodes)) { return false; } } diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index d08681da6ec..de8dc355557 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -14,11 +14,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_spline.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_geometry_set.hh" +#include "BKE_lib_id.h" +#include "BKE_spline.hh" #include "attribute_access_intern.hh" @@ -26,6 +30,7 @@ using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_GSpan; +using blender::fn::GVArrayPtr; using blender::fn::GVMutableArray_For_GMutableSpan; /* -------------------------------------------------------------------- */ @@ -58,6 +63,11 @@ void CurveComponent::clear() if (ownership_ == GeometryOwnershipType::Owned) { delete curve_; } + if (curve_for_render_ != nullptr) { + BKE_id_free(nullptr, curve_for_render_); + curve_for_render_ = nullptr; + } + curve_ = nullptr; } } @@ -118,6 +128,29 @@ void CurveComponent::ensure_owns_direct_data() } } +/** + * Create empty curve data used for rendering the spline's wire edges. + * \note See comment on #curve_for_render_ for further explanation. + */ +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_; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -142,6 +175,171 @@ int CurveComponent::attribute_domain_size(const AttributeDomain domain) const return 0; } +namespace blender::bke { + +namespace { +struct PointIndices { + int spline_index; + int point_index; +}; +} // namespace +static PointIndices lookup_point_indices(Span<int> offsets, const int index) +{ + const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - + offsets.begin() - 1; + const int index_in_spline = index - offsets[spline_index]; + return {spline_index, index_in_spline}; +} + +/** + * Mix together all of a spline'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_spline_impl(const CurveEval &curve, + const VArray<T> &old_values, + MutableSpan<T> r_values) +{ + const int splines_len = curve.splines().size(); + Array<int> offsets = curve.control_point_offsets(); + BLI_assert(r_values.size() == splines_len); + attribute_math::DefaultMixer<T> mixer(r_values); + + for (const int i_spline : IndexRange(splines_len)) { + const int spline_offset = offsets[i_spline]; + const int spline_point_len = offsets[i_spline + 1] - spline_offset; + for (const int i_point : IndexRange(spline_point_len)) { + const T value = old_values[spline_offset + i_point]; + mixer.mix_in(i_spline, value); + } + } + + mixer.finalize(); +} + +static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray) +{ + GVArrayPtr 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(curve.splines().size()); + adapt_curve_domain_point_to_spline_impl<T>(curve, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); + } + }); + return new_varray; +} + +/** + * A virtual array implementation for the conversion of spline attributes to control point + * attributes. The goal is to avoid copying the spline value for every one of its control points + * unless it is necessary (in that case the materialize functions will be called). + */ +template<typename T> class VArray_For_SplineToPoint final : public VArray<T> { + /* Store existing data materialized if it was not already a span. This is expected + * to be worth it because a single spline's value will likely be accessed many times. */ + VArray_Span<T> original_data_; + Array<int> offsets_; + + public: + VArray_For_SplineToPoint(const VArray<T> &original_varray, Array<int> offsets) + : VArray<T>(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets)) + { + } + + T get_impl(const int64_t index) const final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + return original_data_[indices.spline_index]; + } + + void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + const int total_size = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : original_data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]); + } + } + else { + int spline_index = 0; + for (const int dst_index : mask) { + while (offsets_[spline_index] < dst_index) { + spline_index++; + } + r_span[dst_index] = original_data_[spline_index]; + } + } + } + + void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + T *dst = r_span.data(); + const int total_size = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : original_data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]); + } + } + else { + int spline_index = 0; + for (const int dst_index : mask) { + while (offsets_[spline_index] < dst_index) { + spline_index++; + } + new (dst + dst_index) T(original_data_[spline_index]); + } + } + } +}; + +static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray) +{ + GVArrayPtr new_varray; + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { + using T = decltype(dummy); + + Array<int> offsets = curve.control_point_offsets(); + new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>( + offsets.last(), *varray->typed<T>(), std::move(offsets)); + }); + return new_varray; +} + +} // namespace blender::bke + +GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const +{ + if (!varray) { + return {}; + } + if (varray->size() == 0) { + return {}; + } + if (from_domain == to_domain) { + return varray; + } + + if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) { + return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray)); + } + if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) { + return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray)); + } + + return {}; +} + static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); @@ -300,20 +498,6 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) * array implementations try to make it workable in common situations. * \{ */ -namespace { -struct PointIndices { - int spline_index; - int point_index; -}; -} // namespace -static PointIndices lookup_point_indices(Span<int> offsets, const int index) -{ - const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - - offsets.begin() - 1; - const int index_in_spline = index - offsets[spline_index]; - return {spline_index, index_in_spline}; -} - template<typename T> static void point_attribute_materialize(Span<Span<T>> data, Span<int> offsets, @@ -325,14 +509,12 @@ static void point_attribute_materialize(Span<Span<T>> data, for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; - initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset); + r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]); } } else { int spline_index = 0; - for (const int i : r_span.index_range()) { - const int dst_index = mask[i]; - + for (const int dst_index : mask) { while (offsets[spline_index] < dst_index) { spline_index++; } @@ -360,9 +542,7 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, } else { int spline_index = 0; - for (const int i : r_span.index_range()) { - const int dst_index = mask[i]; - + for (const int dst_index : mask) { while (offsets[spline_index] < dst_index) { spline_index++; } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 2ecd0e6bd85..42f3a854aec 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -219,8 +219,7 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { /* We compute all interpolated values at once, because for this interpolation, one has to @@ -249,8 +248,7 @@ static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); /* It is not strictly necessary to compute the value for all corners here. Instead one could * lazily lookup the mesh topology when a specific index accessed. This can be more efficient @@ -290,8 +288,7 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totpoly); @@ -329,8 +326,7 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totedge); @@ -365,8 +361,7 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totvert); @@ -394,8 +389,7 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totloop); @@ -428,8 +422,7 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totedge); @@ -467,8 +460,7 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totpoly); @@ -504,8 +496,7 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totedge); @@ -543,8 +534,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totloop); @@ -576,8 +566,7 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totvert); @@ -615,8 +604,7 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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(mesh.totpoly); @@ -785,18 +773,20 @@ static void set_loop_uv(MLoopUV &uv, float2 co) copy_v2_v2(uv.uv, co); } -static Color4f get_loop_color(const MLoopCol &col) +static ColorGeometry4f get_loop_color(const MLoopCol &col) { - Color4f srgb_color; - rgba_uchar_to_float(srgb_color, &col.r); - Color4f linear_color; - srgb_to_linearrgb_v4(linear_color, srgb_color); + ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a); + ColorGeometry4f linear_color = encoded_color.decode(); return linear_color; } -static void set_loop_color(MLoopCol &col, Color4f linear_color) +static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color) { - linearrgb_to_srgb_uchar4(&col.r, linear_color); + ColorGeometry4b encoded_color = linear_color.encode(); + col.r = encoded_color.r; + col.g = encoded_color.g; + col.b = encoded_color.b; + col.a = encoded_color.a; } static float get_crease(const MEdge &edge) @@ -1133,8 +1123,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>, - make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>); + make_derived_read_attribute<MLoopCol, ColorGeometry4f, get_loop_color>, + make_derived_write_attribute<MLoopCol, ColorGeometry4f, get_loop_color, set_loop_color>); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9abd00c2b4f..69840ba1612 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -544,9 +544,9 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } -static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result) +static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups) { - CurveEval *new_curve = new CurveEval(); + Vector<SplinePtr> new_splines; for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; if (!set.has_curve()) { @@ -558,10 +558,18 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp for (const float4x4 &transform : set_group.transforms) { SplinePtr new_spline = source_spline->copy(); new_spline->transform(transform); - new_curve->add_spline(std::move(new_spline)); + new_splines.append(std::move(new_spline)); } } } + if (new_splines.is_empty()) { + return nullptr; + } + + CurveEval *new_curve = new CurveEval(); + for (SplinePtr &new_spline : new_splines) { + new_curve->add_spline(std::move(new_spline)); + } for (SplinePtr &spline : new_curve->splines()) { /* Spline instances should have no custom attributes, since they always come @@ -573,8 +581,7 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp } new_curve->attributes.reallocate(new_curve->splines().size()); - - result.replace(new_curve); + return new_curve; } static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, @@ -639,14 +646,17 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, { /* Not yet supported. Joining volume grids with the same name requires resampling of at least * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); - UNUSED_VARS(set_groups, dst_component); + UNUSED_VARS(set_groups, result); } static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) { + CurveEval *curve = join_curve_splines(set_groups); + if (curve == nullptr) { + return; + } CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); - join_curve_splines(set_groups, dst_component); + dst_component.replace(curve); } GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 421cb0ac4f1..6d1476485ca 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2624,6 +2624,11 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer continue; } + /* Skip if masks are disabled for this view layer. */ + if (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER) { + continue; + } + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) { if (STREQ(gpl_mask->info, mask->name)) { return true; @@ -2667,6 +2672,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, bGPDframe *act_gpf = gpl->actframe; bGPDframe *sta_gpf = act_gpf; bGPDframe *end_gpf = act_gpf ? act_gpf->next : NULL; + float prev_opacity = gpl->opacity; if (gpl->flag & GP_LAYER_HIDE) { continue; @@ -2682,9 +2688,12 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, * This is used only in final render and never in Viewport. */ if ((view_layer != NULL) && (gpl->viewlayername[0] != '\0') && (!STREQ(view_layer->name, gpl->viewlayername))) { - /* If the layer is used as mask, cannot be filtered or the masking system - * will crash because needs the mask layer in the draw pipeline. */ - if (!gpencil_is_layer_mask(view_layer, gpd, gpl)) { + /* Do not skip masks when rendering the view-layer so that it can still be used to clip + * other layers. Instead set their opacity to zero. */ + if (gpencil_is_layer_mask(view_layer, gpd, gpl)) { + gpl->opacity = 0.0f; + } + else { continue; } } @@ -2779,6 +2788,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, if (layer_cb) { layer_cb(gpl, act_gpf, NULL, thunk); } + gpl->opacity = prev_opacity; continue; } @@ -2816,6 +2826,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, /* If layer solo mode and Paint mode, only keyframes with data are displayed. */ if (GPENCIL_PAINT_MODE(gpd) && (gpl->flag & GP_LAYER_SOLO_MODE) && (act_gpf->framenum != cfra)) { + gpl->opacity = prev_opacity; continue; } @@ -2826,6 +2837,9 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, stroke_cb(gpl, act_gpf, gps, thunk); } } + + /* Restore the opacity in case it was overwritten (used to hide masks in render). */ + gpl->opacity = prev_opacity; } } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index f2893e162cb..073276b7011 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -105,6 +105,11 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK); } +static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id) +{ + return ((Key *)id)->from; +} + static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Key *key = (Key *)id; @@ -216,7 +221,9 @@ IDTypeInfo IDType_ID_KE = { .make_local = NULL, .foreach_id = shapekey_foreach_id, .foreach_cache = NULL, - .owner_get = NULL, /* Could have one actually? */ + /* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also + * share a lot with those (non linkable, only ever used by one owner ID, etc.). */ + .owner_get = shapekey_owner_get, .blend_write = shapekey_blend_write, .blend_read_data = shapekey_blend_read_data, diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index c2e5006cbc1..f26b85338f0 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); id->flag &= ~LIB_INDIRECT_WEAK_LINK; if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -833,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); + /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new + * overrides for recursive resync. */ + BKE_id_new_name_validate(lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -989,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } for (i = 0; i < lb_len; i++) { if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL); + BKE_id_new_name_validate(lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1092,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1221,14 +1223,6 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL); BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_LOCAL) == 0); - if (!is_private_id_data) { - /* When we are handling private ID data, we might still want to manage usercounts, even - * though that ID data-block is actually outside of Main... */ - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || - (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0); - } - /* Never implicitly copy shapekeys when generating temp data outside of Main database. */ - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0); /* 'Private ID' data handling. */ if ((bmain != NULL) && is_private_id_data) { @@ -1565,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * and that current one is not. */ bool is_valid = false; for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && !ID_IS_LINKED(id_test)) { + if (id != id_test && id_test->lib == id->lib) { if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { /* We expect final_name to not be already used, so this is a failure. */ is_valid = false; @@ -1621,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ for (id_test = lb->first; id_test; id_test = id_test->next) { char base_name_test[MAX_ID_NAME - 2]; int number_test; - if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && + if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && (ELEM(id_test->name[base_name_len + 2], '.', '\0')) && STREQLEN(name, id_test->name + 2, base_name_len) && (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == @@ -1710,15 +1704,18 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * + * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * (otherwise, just ensure that it is properly sorted). + * * \return true if a new name had to be created. */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename, but do ensure proper sorting. */ - if (ID_IS_LINKED(id)) { + /* If library, don't rename (unless explicitly required), but do ensure proper sorting. */ + if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); return result; @@ -1762,7 +1759,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) } /* next to indirect usage in read/writefile also in editobject.c scene.c */ -void BKE_main_id_clear_newpoins(Main *bmain) +void BKE_main_id_newptr_and_tag_clear(Main *bmain) { ID *id; @@ -2176,7 +2173,7 @@ void BKE_library_make_local(Main *bmain, TIMEIT_VALUE_PRINT(make_local); #endif - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BLI_memarena_free(linklist_mem); #ifdef DEBUG_TIME @@ -2201,9 +2198,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) /* search for id */ idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); - if (idtest != NULL) { + if (idtest != NULL && !ID_IS_LINKED(idtest)) { /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL); + BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2213,8 +2210,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) */ void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { + BLI_assert(!ID_IS_LINKED(id)); ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name)) { + if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index 9bfd19ae4d6..8e21ae88aa6 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -20,6 +20,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -37,6 +38,7 @@ struct LibIDMainSortTestContext { static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx) { + BKE_idtype_init(); ctx->bmain = BKE_main_new(); } @@ -109,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1) test_lib_id_main_sort_free(&ctx); } +TEST(lib_id_main_unique_name, local_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + + BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_a); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + +TEST(lib_id_main_unique_name, linked_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); + Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + + id_a->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); + id_b->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + id_b->lib = lib_b; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 8341c5b6e78..c93971e7b11 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -48,6 +48,7 @@ #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -80,6 +81,19 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op); 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) +{ + 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; + } + BLI_assert(!"IDTypeInfo of liboverride-embedded ID with no owner getter"); + } + return id->override_library; +} + /** Initialize empty overriding of \a reference_id by \a local_id. */ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id) { @@ -194,12 +208,17 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo *override = NULL; } -static ID *lib_override_library_create_from(Main *bmain, ID *reference_id) +static ID *lib_override_library_create_from(Main *bmain, + ID *reference_id, + const int lib_id_copy_flags) { /* Note: We do not want to copy possible override data from reference here (whether it is an * override template, or already an override of some other ref data). */ - ID *local_id = BKE_id_copy_ex( - bmain, reference_id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE); + ID *local_id = BKE_id_copy_ex(bmain, + reference_id, + NULL, + LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE | + lib_id_copy_flags); if (local_id == NULL) { return NULL; @@ -233,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } + /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just + * assume they are never user-edited, actual proper detection will happen from their owner check. + */ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return false; + } + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) { @@ -257,7 +283,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, BLI_assert(reference_id != NULL); BLI_assert(reference_id->lib != NULL); - ID *local_id = lib_override_library_create_from(bmain, reference_id); + ID *local_id = lib_override_library_create_from(bmain, reference_id, 0); if (do_tagged_remap) { Key *reference_key, *local_key = NULL; @@ -302,9 +328,17 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, * main. You can add more local IDs to be remapped to use new overriding ones by setting their * LIB_TAG_DOIT tag. * + * \param reference_library the library from which the linked data being overridden come from + * (i.e. the library of the linked reference ID). + * + * \param do_no_main Create the new override data outside of Main database. Used for resyncing of + * linked overrides. + * * \return \a true on success, \a false otherwise. */ -bool BKE_lib_override_library_create_from_tag(Main *bmain) +bool BKE_lib_override_library_create_from_tag(Main *bmain, + const Library *reference_library, + const bool do_no_main) { ID *reference_id; bool success = true; @@ -314,7 +348,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) /* Get all IDs we want to override. */ FOREACH_MAIN_ID_BEGIN (bmain, reference_id) { - if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL && + if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library && BKE_idtype_idcode_is_linkable(GS(reference_id->name))) { todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__); todo_id_iter->data = reference_id; @@ -326,10 +360,16 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) /* Override the IDs. */ for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { reference_id = todo_id_iter->data; + + /* If `newid` is already set, assume it has been handled by calling code. + * Only current use case: re-using proxy ID when converting to liboverride. */ if (reference_id->newid == NULL) { - /* If `newid` is already set, assume it has been handled by calling code. - * Only current use case: re-using proxy ID when converting to liboverride. */ - if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) { + /* Note: `no main` case is used during resync procedure, to support recursive resync. + * This requires extra care further down the resync process, + * see: #BKE_lib_override_library_resync. */ + reference_id->newid = lib_override_library_create_from( + bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); + if (reference_id->newid == NULL) { success = false; break; } @@ -369,23 +409,43 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) /* Still checking the whole Main, that way we can tag other local IDs as needing to be * remapped to use newly created overriding IDs, if needed. */ - FOREACH_MAIN_ID_BEGIN (bmain, other_id) { - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) { - /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap - * local IDs usages anyway. */ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + /* In case we created new overrides as 'no main', they are not accessible directly in this + * loop, but we can get to them through their reference's `newid` pointer. */ + if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) { + other_id = id->newid; + /* Otherwise we cannot properly distinguish between IDs that are actually from the + * linked library (and should not be remapped), and IDs that are overrides re-generated + * from the reference from the linked library, and must therefore be remapped. + * + * This is reset afterwards at the end of this loop. */ + other_id->lib = NULL; + } + else { + other_id = id; + } + + /* If other ID is a linked one, but not from the same library as our reference, then we + * consider we should also remap it, as part of recursive resync. */ + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib && + other_id != local_id) { BKE_libblock_relink_ex(bmain, other_id, reference_id, local_id, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); if (reference_key != NULL) { BKE_libblock_relink_ex(bmain, other_id, &reference_key->id, &local_key->id, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); } } + if (other_id != id) { + other_id->lib = reference_id->lib; + } } FOREACH_MAIN_ID_END; } @@ -409,6 +469,8 @@ typedef struct LibOverrideGroupTagData { ID *id_root; uint tag; uint missing_tag; + /* Whether we are looping on override data, or their references (linked) one. */ + bool is_override; } LibOverrideGroupTagData; /* Tag all IDs in dependency relationships within an override hierarchy/group. @@ -421,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa { Main *bmain = data->bmain; ID *id = data->id_root; + const bool is_override = data->is_override; MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); @@ -442,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } /* We only consider IDs from the same library. */ ID *to_id = *to_id_entry->id_pointer.to; - if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) { - /* Pure local data is a barrier of dependency in override cases. */ + if (to_id == NULL || to_id->lib != id->lib || + (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) { + /* IDs from different libraries, or non-override IDs in case we are processing overrides, are + * both barriers of dependency. */ continue; } - if (to_id != NULL && to_id->lib == id->lib) { - LibOverrideGroupTagData sub_data = *data; - sub_data.id_root = to_id; - if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { - id->tag |= data->tag; - } + LibOverrideGroupTagData sub_data = *data; + sub_data.id_root = to_id; + if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { + id->tag |= data->tag; } } @@ -463,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_LINKED(id_owner)); + BLI_assert(!data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -528,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_root = data->id_root; + BLI_assert(!data->is_override); if ((id_root->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -554,11 +619,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } } -static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner)); + BLI_assert(data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -585,45 +651,37 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data if (ELEM(to_id, NULL, id_owner)) { continue; } - if (!ID_IS_OVERRIDE_LIBRARY(to_id) || ID_IS_LINKED(to_id)) { + if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) { continue; } - /* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case - * above). */ - if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) { - Library *reference_lib = NULL; - if (GS(id_owner->name) == ID_KE) { - reference_lib = ((Key *)id_owner)->from->override_library->reference->lib; - } - else { - reference_lib = id_owner->override_library->reference->lib; - } - if (to_id->override_library->reference->lib != reference_lib) { - /* We do not override data-blocks from other libraries, nor do we process them. */ - continue; - } + Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib; + ID *to_id_reference = lib_override_get(bmain, to_id)->reference; + if (to_id_reference->lib != reference_lib) { + /* We do not override data-blocks from other libraries, nor do we process them. */ + continue; + } - if (to_id->override_library->reference->tag & LIB_TAG_MISSING) { - to_id->tag |= missing_tag; - } - else { - to_id->tag |= tag; - } + if (to_id_reference->tag & LIB_TAG_MISSING) { + to_id->tag |= missing_tag; + } + else { + to_id->tag |= tag; } /* Recursively process the dependencies. */ LibOverrideGroupTagData sub_data = *data; sub_data.id_root = to_id; - lib_override_local_group_tag_recursive(&sub_data); + lib_override_overrides_group_tag_recursive(&sub_data); } } /* This will tag all override IDs of an override group defined by the given `id_root`. */ -static void lib_override_local_group_tag(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root) && !ID_IS_LINKED(id_root)); + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + BLI_assert(data->is_override); if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -633,14 +691,17 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) } /* Tag all local overrides in id_root's group. */ - lib_override_local_group_tag_recursive(data); + lib_override_overrides_group_tag_recursive(data); } static bool lib_override_library_create_do(Main *bmain, ID *id_root) { BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -648,7 +709,7 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root) BKE_main_relations_free(bmain); - return BKE_lib_override_library_create_from_tag(bmain); + return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false); } static void lib_override_library_create_post_process(Main *bmain, @@ -659,6 +720,9 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *residual_storage, const bool is_resync) { + /* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we + * do not do anything about it. */ + BKE_main_collection_sync(bmain); /* We create a set of all objects referenced into the scene by its hierarchy of collections. @@ -668,7 +732,7 @@ static void lib_override_library_create_post_process(Main *bmain, /* Instantiating the root collection or object should never be needed in resync case, since the * old override would be remapped to the new one. */ - if (!is_resync && id_root != NULL && id_root->newid != NULL) { + if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) { switch (GS(id_root->name)) { case ID_GR: { Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? @@ -713,56 +777,58 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *default_instantiating_collection = residual_storage; LISTBASE_FOREACH (Object *, ob, &bmain->objects) { Object *ob_new = (Object *)ob->id.newid; - if (ob_new != NULL) { - BLI_assert(ob_new->id.override_library != NULL && - ob_new->id.override_library->reference == &ob->id); - - if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) { - if (id_root != NULL && default_instantiating_collection == NULL) { - ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; - switch (GS(id_ref->name)) { - case ID_GR: { - /* Adding the object to a specific collection outside of the root overridden one is a - * fairly bad idea (it breaks the override hierarchy concept). But there is no other - * way to do this currently (we cannot add new collections to overridden root one, - * this is not currently supported). - * Since that will be fairly annoying and noisy, only do that in case the override - * object is not part of any existing collection (i.e. its user count is 0). In - * practice this should never happen I think. */ - if (ID_REAL_USERS(ob_new) != 0) { - continue; - } - default_instantiating_collection = BKE_collection_add( - bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); - /* Hide the collection from viewport and render. */ - default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; - break; + if (ob_new == NULL || ob_new->id.lib != NULL) { + continue; + } + + BLI_assert(ob_new->id.override_library != NULL && + ob_new->id.override_library->reference == &ob->id); + + if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) { + if (id_root != NULL && default_instantiating_collection == NULL) { + ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; + switch (GS(id_ref->name)) { + case ID_GR: { + /* Adding the object to a specific collection outside of the root overridden one is a + * fairly bad idea (it breaks the override hierarchy concept). But there is no other + * way to do this currently (we cannot add new collections to overridden root one, + * this is not currently supported). + * Since that will be fairly annoying and noisy, only do that in case the override + * object is not part of any existing collection (i.e. its user count is 0). In + * practice this should never happen I think. */ + if (ID_REAL_USERS(ob_new) != 0) { + continue; } - case ID_OB: { - /* Add the other objects to one of the collections instantiating the - * root object, or scene's master collection if none found. */ - Object *ob_ref = (Object *)id_ref; - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_has_object(collection, ob_ref) && - BKE_view_layer_has_collection(view_layer, collection) && - !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { - default_instantiating_collection = collection; - } + default_instantiating_collection = BKE_collection_add( + bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); + /* Hide the collection from viewport and render. */ + default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | + COLLECTION_RESTRICT_RENDER; + break; + } + case ID_OB: { + /* Add the other objects to one of the collections instantiating the + * root object, or scene's master collection if none found. */ + Object *ob_ref = (Object *)id_ref; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, ob_ref) && + BKE_view_layer_has_collection(view_layer, collection) && + !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { + default_instantiating_collection = collection; } - break; } - default: - BLI_assert(0); + break; } + default: + BLI_assert(0); } - if (default_instantiating_collection == NULL) { - default_instantiating_collection = scene->master_collection; - } - - BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); - DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } + if (default_instantiating_collection == NULL) { + default_instantiating_collection = scene->master_collection; + } + + BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } } @@ -772,7 +838,7 @@ static void lib_override_library_create_post_process(Main *bmain, /** * Advanced 'smart' function to create fully functional overrides. * - * \note Currently it only does special things if given \a id_root is an object of collection, more + * \note Currently it only does special things if given \a id_root is an object or collection, more * specific behaviors may be added in the future for other ID types. * * \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at @@ -782,22 +848,35 @@ static void lib_override_library_create_post_process(Main *bmain, * \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 r_id_root_override if not NULL, the override generated for the given \a id_root. * \return true if override was successfully created. */ -bool BKE_lib_override_library_create( - Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +bool BKE_lib_override_library_create(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ID *id_root, + ID *id_reference, + 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, id_root); if (!success) { return success; } + if (r_id_root_override != NULL) { + *r_id_root_override = id_root->newid; + } + lib_override_library_create_post_process( bmain, scene, view_layer, id_root, id_reference, NULL, false); /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ @@ -862,7 +941,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); - return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference); + return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); } /** @@ -882,17 +961,20 @@ bool BKE_lib_override_library_resync(Main *bmain, ReportList *reports) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); - BLI_assert(!ID_IS_LINKED(id_root)); ID *id_root_reference = id_root->override_library->reference; BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; - lib_override_local_group_tag(&data); + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; + lib_override_overrides_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; + data.is_override = false; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -916,12 +998,35 @@ bool BKE_lib_override_library_resync(Main *bmain, id->tag |= LIB_TAG_MISSING; } - if (id->tag & LIB_TAG_DOIT && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) { /* 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. */ - if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) { - BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id); + IDOverrideLibrary *id_override_library = lib_override_get(bmain, id); + ID *reference_id = id_override_library->reference; + if (GS(reference_id->name) != GS(id->name)) { + switch (GS(id->name)) { + case ID_KE: + reference_id = (ID *)BKE_key_from_id(reference_id); + break; + case ID_GR: + BLI_assert(GS(reference_id->name) == ID_SCE); + reference_id = (ID *)((Scene *)reference_id)->master_collection; + break; + case ID_NT: + reference_id = (ID *)ntreeFromID(id); + break; + default: + break; + } + } + BLI_assert(GS(reference_id->name) == GS(id->name)); + + if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) { + BLI_ghash_insert(linkedref_to_old_override, reference_id, id); + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + continue; + } if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) { /* We have an override, but now it does not seem to be necessary to override that ID * anymore. Check if there are some actual overrides from the user, otherwise assume @@ -960,7 +1065,8 @@ bool BKE_lib_override_library_resync(Main *bmain, /* Note that this call also remaps all pointers of tagged IDs from old override IDs to new * 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); + const bool success = BKE_lib_override_library_create_from_tag( + bmain, id_root_reference->lib, true); if (!success) { return success; @@ -969,55 +1075,104 @@ bool BKE_lib_override_library_resync(Main *bmain, ListBase *lb; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0); + /* We need to 'move back' newly created override into its proper library (since it was + * duplicated from the reference ID with 'no main' option, it should currently be the same + * as the reference ID one). */ + BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib); + BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib); + id_override_new->lib = id_root->lib; + /* Remap step below will tag directly linked ones properly as needed. */ + if (ID_IS_LINKED(id_override_new)) { + id_override_new->tag |= LIB_TAG_INDIRECT; + } + if (id_override_old != NULL) { /* Swap the names between old override ID and new one. */ char id_name_buf[MAX_ID_NAME]; memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf)); memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name)); memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name)); - /* Note that this is a very efficient way to keep BMain IDs ordered as expected after - * swapping their names. - * However, one has to be very careful with this when iterating over the listbase at the - * same time. Here it works because we only execute this code when we are in the linked - * IDs, which are always *after* all local ones, and we only affect local IDs. */ - BLI_listbase_swaplinks(lb, id_override_old, id_override_new); - - /* Remap the whole local IDs to use the new override. */ - BKE_libblock_remap( - bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE); - - /* Copy over overrides rules from old override ID to new one. */ - BLI_duplicatelist(&id_override_new->override_library->properties, - &id_override_old->override_library->properties); - for (IDOverrideLibraryProperty * - op_new = id_override_new->override_library->properties.first, - *op_old = id_override_old->override_library->properties.first; - op_new; - op_new = op_new->next, op_old = op_old->next) { - lib_override_library_property_copy(op_new, op_old); + + BLI_insertlinkreplace(lb, id_override_old, id_override_new); + id_override_old->tag |= LIB_TAG_NO_MAIN; + id_override_new->tag &= ~LIB_TAG_NO_MAIN; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)); + + /* Copy over overrides rules from old override ID to new one. */ + BLI_duplicatelist(&id_override_new->override_library->properties, + &id_override_old->override_library->properties); + IDOverrideLibraryProperty *op_new = + id_override_new->override_library->properties.first; + IDOverrideLibraryProperty *op_old = + id_override_old->override_library->properties.first; + for (; op_new; op_new = op_new->next, op_old = op_old->next) { + lib_override_library_property_copy(op_new, op_old); + } } } + else { + /* Add to proper main list, ensure unique name for local ID, sort, and clear relevant + * tags. */ + BKE_libblock_management_main_add(bmain, id_override_new); + } } } FOREACH_MAIN_LISTBASE_ID_END; } FOREACH_MAIN_LISTBASE_END; + /* We need to remap old to new override usages in a separate loop, after all new overrides have + * been added to Main. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + ID *id_override_new = id->newid; + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + + if (id_override_old != NULL) { + /* Remap all IDs to use the new override. */ + BKE_libblock_remap(bmain, id_override_old, id_override_new, 0); + /* Remap no-main override IDs we just created too. */ + GHashIterator linkedref_to_old_override_iter; + GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { + ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); + if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) { + BKE_libblock_relink_ex(bmain, + id_override_old_iter, + id_override_old, + id_override_new, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); + } + } + } + } + } + FOREACH_MAIN_ID_END; + + BKE_main_collection_sync(bmain); + /* We need to apply override rules in a separate loop, after all ID pointers have been properly * remapped, and all new local override IDs have gotten their proper original names, otherwise * override operations based on those ID names would fail. */ FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) { + continue; + } ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); - if (id_override_old != NULL) { + if (id_override_old == NULL) { + continue; + } + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) { /* Apply rules on new override ID using old one as 'source' data. */ /* Note that since we already remapped ID pointers in old override IDs to new ones, we * can also apply ID pointer override rules safely here. */ @@ -1051,30 +1206,44 @@ bool BKE_lib_override_library_resync(Main *bmain, RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS : RNA_OVERRIDE_APPLY_FLAG_NOP); } + + /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages + * of the old one. + * This is necessary in case said old ID is not in Main anymore. */ + BKE_libblock_relink_ex(bmain, + id_override_old, + NULL, + NULL, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); + id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT; } } FOREACH_MAIN_ID_END; /* Delete old override IDs. - * Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT. - * This improves performances anyway, so everything is fine. */ + * Note that we have to use tagged group deletion here, since ID deletion also uses + * LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */ int user_edited_overrides_deletion_count = 0; FOREACH_MAIN_ID_BEGIN (bmain, id) { if (id->tag & LIB_TAG_DOIT) { - /* Note that this works because linked IDs are always after local ones (including overrides), - * so we will only ever tag an old override ID after we have already checked it in this loop, - * hence we cannot untag it later. */ - if (id->newid != NULL && ID_IS_LINKED(id)) { + /* Note that this works because linked IDs are always after local ones (including + * overrides), so we will only ever tag an old override ID after we have already checked it + * in this loop, hence we cannot untag it later. */ + if (id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); if (id_override_old != NULL) { id->newid->tag &= ~LIB_TAG_DOIT; id_override_old->tag |= LIB_TAG_DOIT; + if (id_override_old->tag & LIB_TAG_NO_MAIN) { + BKE_id_free(bmain, id_override_old); + } } } id->tag &= ~LIB_TAG_DOIT; } - /* Also deal with old overrides that went missing in new linked data. */ + /* Also deal with old overrides that went missing in new linked data - only for real local + * overrides for now, not those who are linked. */ else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) { BLI_assert(ID_IS_OVERRIDE_LIBRARY(id)); if (!BKE_lib_override_library_is_user_edited(id)) { @@ -1105,6 +1274,10 @@ bool BKE_lib_override_library_resync(Main *bmain, } } FOREACH_MAIN_ID_END; + + /* Cleanup, many pointers in this GHash are already invalid now. */ + BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BKE_id_multi_tagged_delete(bmain); /* At this point, `id_root` has very likely been deleted, we need to update it to its new @@ -1137,50 +1310,136 @@ bool BKE_lib_override_library_resync(Main *bmain, } /* Cleanup. */ - BLI_ghash_free(linkedref_to_old_override, NULL, NULL); - - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */ return success; } -/** - * Detect and handle required resync of overrides data, when relations between reference linked IDs - * have changed. +/* Also tag ancestors overrides for resync. * - * This is a fairly complex and costly operation, typically it should be called after - * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases. + * WARNING: Expects `bmain` to have valid relation data. * - * This function will first detect the remaining cases requiring a resync (namely, either when an - * existing linked ID that did not require to be overridden before now would be, or when new IDs - * are added to the hierarchy). + * NOTE: Related to `lib_override_library_main_resync_find_root_recurse` below. * - * Then it will handle the resync of necessary IDs (through calls to - * #BKE_lib_override_library_resync). + * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to + * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and + * resync the whole hierarchy. */ -void BKE_lib_override_library_main_resync(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - ReportList *reports) +static void lib_override_resync_tagging_finalize_recurse(Main *bmain, + ID *id, + const int library_indirect_level) { - /* We use a specific collection to gather/store all 'orphaned' override collections and objects - * generated by re-sync-process. This avoids putting them in scene's master collection. */ -#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS" - Collection *override_resync_residual_storage = BLI_findstring( - &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2); - if (override_resync_residual_storage != NULL && - override_resync_residual_storage->id.lib != NULL) { - override_resync_residual_storage = NULL; + if (id->lib != NULL && id->lib->temp_index > library_indirect_level) { + CLOG_ERROR( + &LOG, + "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " + "as needing resync.", + library_indirect_level, + id->name, + id->lib->filepath, + id->lib->temp_index); } - if (override_resync_residual_storage == NULL) { - override_resync_residual_storage = BKE_collection_add( - bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); - /* Hide the collection from viewport and render. */ - override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); + BLI_assert(entry != NULL); + + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + /* This ID has already been processed. */ + return; + } + /* This way we won't process again that ID, should we encounter it again through another + * relationship hierarchy. */ + entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; + + for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL; + entry_item = entry_item->next) { + if (entry_item->usage_flag & + (IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) { + continue; + } + ID *id_from = entry_item->id_pointer.from; + + /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */ + if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) { + id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + CLOG_INFO(&LOG, + 4, + "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to " + "be overridden", + id_from->name, + id_from->lib, + id->name, + id->lib); + lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level); + } + } +} + +/* Ensures parent collection (or objects) in the same override group are also tagged for resync. + * + * This is needed since otherwise, some (new) ID added in one sub-collection might be used in + * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those + * sub-collections would be unaware that this is the same ID, and would re-generate several + * overrides for it. + * + * NOTE: Related to `lib_override_resync_tagging_finalize` above. + */ +static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level) +{ + (*level)++; + ID *return_id = id; + + switch (GS(id->name)) { + case ID_GR: { + /* Find the highest valid collection in the parenting hierarchy. + * Note that in practice, in any decent common case there is only one well defined root + * collection anyway. */ + int max_level = *level; + Collection *collection = (Collection *)id; + LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) { + Collection *collection_parent = collection_parent_iter->collection; + if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) && + collection_parent->id.lib == id->lib) { + int tmp_level = *level; + ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id, + &tmp_level); + if (tmp_level > max_level) { + max_level = tmp_level; + return_id = tmp_id; + } + } + } + break; + } + case ID_OB: { + Object *object = (Object *)id; + if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) && + object->parent->id.lib == id->lib) { + return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level); + } + break; + } + default: + break; } + return return_id; +} + +/* Ensure resync of all overrides at one level of indirect usage. + * + * We need to handle each level independently, since an override at level n may be affected by + * other overrides from level n + 1 etc. (i.e. from linked overrides it may use). + */ +static void lib_override_library_main_resync_on_library_indirect_level( + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Collection *override_resync_residual_storage, + const int library_indirect_level, + ReportList *reports) +{ BKE_main_relations_create(bmain, 0); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); @@ -1192,7 +1451,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, * those used by current existing overrides. */ ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { continue; } if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) { @@ -1203,7 +1462,8 @@ void BKE_lib_override_library_main_resync(Main *bmain, LibOverrideGroupTagData data = {.bmain = bmain, .id_root = id->override_library->reference, .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING}; + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); @@ -1213,13 +1473,15 @@ void BKE_lib_override_library_main_resync(Main *bmain, /* Now check existing overrides, those needing resync will be the one either already tagged as * such, or the one using linked data that is now tagged as needing override. */ + BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { continue; } if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) { - CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name); + CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib); + lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level); continue; } @@ -1234,14 +1496,17 @@ void BKE_lib_override_library_main_resync(Main *bmain, ID *id_to = *entry_item->id_pointer.to; /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */ - if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) { + if (ID_IS_LINKED(id_to) && (id_to->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) { id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; CLOG_INFO(&LOG, 3, - "ID %s now tagged as needing resync because they use linked %s that now needs " - "to be overridden", + "ID %s (%p) now tagged as needing resync because they use linked %s (%p) that " + "now needs to be overridden", id->name, - id_to->name); + id->lib, + id_to->name, + id_to->lib); + lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level); break; } } @@ -1256,27 +1521,29 @@ void BKE_lib_override_library_main_resync(Main *bmain, * `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */ bool do_continue = true; while (do_continue) { - ListBase *lb; do_continue = false; + ListBase *lb; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) { - continue; - } - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); - if (ID_IS_LINKED(id)) { + if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 || + (ID_IS_LINKED(id) && id->lib->temp_index < library_indirect_level) || + (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } - do_continue = true; + int level = 0; /* In complex non-supported cases, with several different override hierarchies sharing * relations between each-other, we may end up not actually updating/replacing the given * root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites * project repository, r2687). * This can lead to infinite loop here, at least avoid this. */ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + id = lib_override_library_main_resync_find_root_recurse(id, &level); + id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); + do_continue = true; - CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name); + CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib); const bool success = BKE_lib_override_library_resync( bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports); CLOG_INFO(&LOG, 2, "\tSuccess: %d", success); @@ -1289,6 +1556,113 @@ void BKE_lib_override_library_main_resync(Main *bmain, } FOREACH_MAIN_LISTBASE_END; } +} + +static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) +{ + if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) { + return IDWALK_RET_NOP; + } + ID *id_owner = cb_data->id_owner; + ID *id = *cb_data->id_pointer; + if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { + const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0; + if (owner_library_indirect_level > 10000) { + CLOG_ERROR( + &LOG, + "Levels of indirect usages of libraries is way too high, skipping further building " + "loops (Involves at least '%s' and '%s')", + id_owner->lib->filepath, + id->lib->filepath); + BLI_assert(0); + return IDWALK_RET_NOP; + } + + if (owner_library_indirect_level >= id->lib->temp_index) { + id->lib->temp_index = owner_library_indirect_level + 1; + *(bool *)cb_data->user_data = true; + } + } + return IDWALK_RET_NOP; +} + +/** Define the `temp_index` of libraries from their highest level of indirect usage. + * + * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of + * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */ +static int lib_override_libraries_index_define(Main *bmain) +{ + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + /* index 0 is reserved for local data. */ + library->temp_index = 1; + } + bool do_continue = true; + while (do_continue) { + do_continue = false; + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + BKE_library_foreach_ID_link( + bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY); + } + FOREACH_MAIN_ID_END; + } + + int library_indirect_level_max = 0; + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + if (library->temp_index > library_indirect_level_max) { + library_indirect_level_max = library->temp_index; + } + } + return library_indirect_level_max; +} + +/** + * Detect and handle required resync of overrides data, when relations between reference linked IDs + * have changed. + * + * This is a fairly complex and costly operation, typically it should be called after + * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases. + * + * This function will first detect the remaining cases requiring a resync (namely, either when an + * existing linked ID that did not require to be overridden before now would be, or when new IDs + * are added to the hierarchy). + * + * Then it will handle the resync of necessary IDs (through calls to + * #BKE_lib_override_library_resync). + */ +void BKE_lib_override_library_main_resync(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ReportList *reports) +{ + /* We use a specific collection to gather/store all 'orphaned' override collections and objects + * generated by re-sync-process. This avoids putting them in scene's master collection. */ +#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS" + Collection *override_resync_residual_storage = BLI_findstring( + &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2); + if (override_resync_residual_storage != NULL && + override_resync_residual_storage->id.lib != NULL) { + override_resync_residual_storage = NULL; + } + if (override_resync_residual_storage == NULL) { + override_resync_residual_storage = BKE_collection_add( + bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); + /* Hide the collection from viewport and render. */ + override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT | + COLLECTION_RESTRICT_RENDER; + } + + int library_indirect_level = lib_override_libraries_index_define(bmain); + while (library_indirect_level >= 0) { + /* Update overrides from each indirect level separately. */ + lib_override_library_main_resync_on_library_indirect_level(bmain, + scene, + view_layer, + override_resync_residual_storage, + library_indirect_level, + reports); + library_indirect_level--; + } /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ lib_override_library_create_post_process( @@ -1313,9 +1687,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; - lib_override_local_group_tag(&data); + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; + lib_override_overrides_group_tag(&data); BKE_main_relations_free(bmain); @@ -1961,7 +2338,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH); FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && + if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) && (force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) { /* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this * function is called. */ diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 3281783d81a..b748061ef8a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -244,9 +244,10 @@ static void library_foreach_ID_link(Main *bmain, * (the node tree), but re-use those generated for the 'owner' ID (the material). */ if (inherit_data == NULL) { data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0; - /* When an ID is not in Main database, it should never refcount IDs it is using. - * Exceptions: NodeTrees (yeah!) directly used by Materials. */ - data.cb_flag_clear = (id->tag & LIB_TAG_NO_MAIN) ? IDWALK_CB_USER | IDWALK_CB_USER_ONE : 0; + /* When an ID is defined as not refcounting its ID usages, it should never do it. */ + data.cb_flag_clear = (id->tag & LIB_TAG_NO_USER_REFCOUNT) ? + IDWALK_CB_USER | IDWALK_CB_USER_ONE : + 0; } else { data.cb_flag = inherit_data->cb_flag; diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index b32b97dc250..2641208897e 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -137,6 +137,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; + const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0; #ifdef DEBUG_PRINT printf( @@ -203,16 +204,16 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) } } if (cb_flag & IDWALK_CB_USER) { - /* NOTE: We don't user-count IDs which are not in the main database. + /* NOTE: by default we don't user-count IDs which are not in the main database. * This is because in certain conditions we can have data-blocks in * the main which are referencing data-blocks outside of it. * For example, BKE_mesh_new_from_object() called on an evaluated * object will cause such situation. */ - if ((old_id->tag & LIB_TAG_NO_MAIN) == 0) { + if (force_user_refcount || (old_id->tag & LIB_TAG_NO_MAIN) == 0) { id_us_min(old_id); } - if (new_id != NULL && (new_id->tag & LIB_TAG_NO_MAIN) == 0) { + if (new_id != NULL && (force_user_refcount || (new_id->tag & LIB_TAG_NO_MAIN) == 0)) { /* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */ new_id->us++; } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 73b64e6efb3..468f735257a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -770,6 +770,7 @@ int BKE_object_material_count_eval(Object *ob) void BKE_id_material_eval_assign(ID *id, int slot, Material *material) { + BLI_assert(slot >= 1); Material ***materials_ptr = BKE_id_material_array_p(id); short *len_ptr = BKE_id_material_len_p(id); if (ELEM(NULL, materials_ptr, len_ptr)) { @@ -793,6 +794,21 @@ void BKE_id_material_eval_assign(ID *id, int slot, Material *material) (*materials_ptr)[slot_index] = material; } +/** + * Add an empty material slot if the id has no material slots. This material slot allows the + * material to be overwritten by object-linked materials. + */ +void BKE_id_material_eval_ensure_default_slot(ID *id) +{ + short *len_ptr = BKE_id_material_len_p(id); + if (len_ptr == NULL) { + return; + } + if (*len_ptr == 0) { + BKE_id_material_eval_assign(id, 1, NULL); + } +} + Material *BKE_gpencil_material(Object *ob, short act) { Material *ma = BKE_object_material_get(ob, act); @@ -1497,16 +1513,16 @@ void BKE_texpaint_slots_refresh_object(Scene *scene, struct Object *ob) } struct FindTexPaintNodeData { - bNode *node; - short iter_index; - short index; + Image *ima; + bNode *r_node; }; static bool texpaint_slot_node_find_cb(bNode *node, void *userdata) { struct FindTexPaintNodeData *find_data = userdata; - if (find_data->iter_index++ == find_data->index) { - find_data->node = node; + Image *ima = (Image *)node->id; + if (find_data->ima == ima) { + find_data->r_node = node; return false; } @@ -1515,10 +1531,10 @@ static bool texpaint_slot_node_find_cb(bNode *node, void *userdata) bNode *BKE_texpaint_slot_material_find_node(Material *ma, short texpaint_slot) { - struct FindTexPaintNodeData find_data = {NULL, 0, texpaint_slot}; + struct FindTexPaintNodeData find_data = {ma->texpaintslot[texpaint_slot].ima, NULL}; ntree_foreach_texnode_recursive(ma->nodetree, texpaint_slot_node_find_cb, &find_data); - return find_data.node; + return find_data.r_node; } /* r_col = current value, col = new value, (fac == 0) is no change */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 643dc58af18..2a509fd7df4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -513,7 +513,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->storage) { /* could be handlerized at some point, now only 1 exception still */ - if ((ntree->type == NTREE_SHADER) && + if ((ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY)) && ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } @@ -5050,8 +5050,10 @@ static void registerGeometryNodes() register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); + register_node_type_geo_curve_length(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_resample(); + register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); register_node_type_geo_input_material(); register_node_type_geo_is_viewport(); @@ -5066,6 +5068,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_ico_sphere(); register_node_type_geo_mesh_primitive_line(); register_node_type_geo_mesh_primitive_uv_sphere(); + register_node_type_geo_mesh_to_curve(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc index 7a28fd295fb..e5e9f00c7c3 100644 --- a/source/blender/blenkernel/intern/node_ui_storage.cc +++ b/source/blender/blenkernel/intern/node_ui_storage.cc @@ -39,7 +39,7 @@ using blender::Vector; * bNodeTree struct in DNA. This could change if the node tree had a runtime struct. */ static std::mutex global_ui_storage_mutex; -static void ui_storage_ensure(bNodeTree &ntree) +static NodeTreeUIStorage &ui_storage_ensure(bNodeTree &ntree) { /* As an optimization, only acquire a lock if the UI storage doesn't exist, * because it only needs to be allocated once for every node tree. */ @@ -50,6 +50,7 @@ static void ui_storage_ensure(bNodeTree &ntree) ntree.ui_storage = new NodeTreeUIStorage(); } } + return *ntree.ui_storage; } const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C, @@ -90,7 +91,7 @@ void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree, { NodeTreeUIStorage *ui_storage = ntree.ui_storage; if (ui_storage != nullptr) { - std::lock_guard<std::mutex> lock(ui_storage->context_map_mutex); + std::lock_guard<std::mutex> lock(ui_storage->mutex); ui_storage->context_map.remove(context); } } @@ -126,20 +127,14 @@ static void node_error_message_log(bNodeTree &ntree, } } -static NodeUIStorage &node_ui_storage_ensure(bNodeTree &ntree, +static NodeUIStorage &node_ui_storage_ensure(NodeTreeUIStorage &locked_ui_storage, const NodeTreeEvaluationContext &context, const bNode &node) { - ui_storage_ensure(ntree); - NodeTreeUIStorage &ui_storage = *ntree.ui_storage; - - std::lock_guard<std::mutex> lock(ui_storage.context_map_mutex); Map<std::string, NodeUIStorage> &node_tree_ui_storage = - ui_storage.context_map.lookup_or_add_default(context); - + locked_ui_storage.context_map.lookup_or_add_default(context); NodeUIStorage &node_ui_storage = node_tree_ui_storage.lookup_or_add_default_as( StringRef(node.name)); - return node_ui_storage; } @@ -149,10 +144,12 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree, const NodeWarningType type, std::string message) { + NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree); + std::lock_guard lock{ui_storage.mutex}; + node_error_message_log(ntree, node, message, type); - NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node); - std::lock_guard lock{node_ui_storage.mutex}; + NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node); node_ui_storage.warnings.append({type, std::move(message)}); } @@ -163,8 +160,10 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree, const AttributeDomain domain, const CustomDataType data_type) { - NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node); - std::lock_guard lock{node_ui_storage.mutex}; + NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree); + std::lock_guard lock{ui_storage.mutex}; + + NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node); node_ui_storage.attribute_hints.add_as( AvailableAttributeInfo{attribute_name, domain, data_type}); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 034af924ab1..b73f6a5b78c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2369,7 +2369,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f BLI_listbase_clear(&psysn->pathcachebufs); BLI_listbase_clear(&psysn->childcachebufs); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview * creation. */ // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0); @@ -2622,8 +2622,8 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) * * \note This function does not do any remapping to new IDs, caller must do it * (\a #BKE_libblock_relink_to_newid()). - * \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates - * of DEG too (#DAG_relations_tag_update()). + * \note Caller MUST free \a newid pointers itself (#BKE_main_id_newptr_and_tag_clear()) and call + * updates of DEG too (#DAG_relations_tag_update()). */ Object *BKE_object_duplicate(Main *bmain, Object *ob, @@ -2633,8 +2633,7 @@ Object *BKE_object_duplicate(Main *bmain, const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ if (ID_IS_LINKED(ob)) { @@ -2773,8 +2772,7 @@ Object *BKE_object_duplicate(Main *bmain, #endif /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } if (obn->type == OB_ARMATURE) { diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index c727a144c87..ce4be411c9a 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -4912,9 +4912,12 @@ void particle_system_update(struct Depsgraph *depsgraph, sim.psmd->flag |= eParticleSystemFlag_Pars; } + ParticleTexture ptex; + LOOP_EXISTING_PARTICLES { - pa->size = part->size; + psys_get_texture(&sim, pa, &ptex, PAMAP_SIZE, cfra); + pa->size = part->size * ptex.size; if (part->randsize > 0.0f) { pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1); } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a4ab64a8a02..a58db89ad0c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1985,8 +1985,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) const bool is_subprocess = false; if (!is_subprocess) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and * duplicate all expected linked data. */ if (ID_IS_LINKED(sce)) { @@ -2027,8 +2026,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) #endif /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_collection_sync(bmain); } diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 4be3ba8576e..3c6cf2c78cf 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -31,6 +31,14 @@ SplinePtr BezierSpline::copy() const return std::make_unique<BezierSpline>(*this); } +SplinePtr BezierSpline::copy_settings() const +{ + std::unique_ptr<BezierSpline> copy = std::make_unique<BezierSpline>(); + copy_base_settings(*this, *copy); + copy->resolution_ = resolution_; + return copy; +} + int BezierSpline::size() const { const int size = positions_.size(); @@ -59,18 +67,18 @@ void BezierSpline::set_resolution(const int value) * \warning Call #reallocate on the spline's attributes after adding all points. */ void BezierSpline::add_point(const float3 position, - const HandleType handle_type_start, - const float3 handle_position_start, - const HandleType handle_type_end, - const float3 handle_position_end, + const HandleType handle_type_left, + const float3 handle_position_left, + const HandleType handle_type_right, + const float3 handle_position_right, const float radius, const float tilt) { - handle_types_left_.append(handle_type_start); - handle_positions_left_.append(handle_position_start); + handle_types_left_.append(handle_type_left); + handle_positions_left_.append(handle_position_left); positions_.append(position); - handle_types_right_.append(handle_type_end); - handle_positions_right_.append(handle_position_end); + handle_types_right_.append(handle_type_right); + handle_positions_right_.append(handle_position_right); radii_.append(radius); tilts_.append(tilt); this->mark_cache_invalid(); diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index ae691d26cdb..cae206341a0 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -32,6 +32,16 @@ SplinePtr NURBSpline::copy() const return std::make_unique<NURBSpline>(*this); } +SplinePtr NURBSpline::copy_settings() const +{ + std::unique_ptr<NURBSpline> copy = std::make_unique<NURBSpline>(); + copy_base_settings(*this, *copy); + copy->knots_mode = knots_mode; + copy->resolution_ = resolution_; + copy->order_ = order_; + return copy; +} + int NURBSpline::size() const { const int size = positions_.size(); diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 5c33b0052fc..5f8e81d5ad0 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -28,6 +28,13 @@ SplinePtr PolySpline::copy() const return std::make_unique<PolySpline>(*this); } +SplinePtr PolySpline::copy_settings() const +{ + std::unique_ptr<PolySpline> copy = std::make_unique<PolySpline>(); + copy_base_settings(*this, *copy); + return copy; +} + int PolySpline::size() const { const int size = positions_.size(); diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 9ae1c754846..3612a26315c 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -943,7 +943,7 @@ static int unit_scale_str(char *str, /* Add the addition sign, the bias, and the close parenthesis after the value. */ int value_end_ofs = find_end_of_value_chars(str, len_max, prev_op_ofs + 2); - int len_bias_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias); + int len_bias_num = BLI_snprintf_rlen(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias); if (value_end_ofs + len_bias_num < len_max) { memmove(str + value_end_ofs + len_bias_num, str + value_end_ofs, len - value_end_ofs + 1); memcpy(str + value_end_ofs, str_tmp, len_bias_num); @@ -957,7 +957,8 @@ static int unit_scale_str(char *str, int len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */ /* "#" Removed later */ - int len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); + int len_num = BLI_snprintf_rlen( + str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); if (len_num > len_max) { len_num = len_max; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 39f65d76e3c..92d19f18a93 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -205,12 +205,11 @@ static int write_audio_frame(FFMpegContext *context) success = -1; } - av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); - if (pkt->duration > 0) { - pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base); - } - pkt->stream_index = context->audio_stream->index; + av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->audio_stream, pkt); +# endif pkt->flags |= AV_PKT_FLAG_KEY; @@ -349,6 +348,10 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif + if (av_interleaved_write_frame(context->outfile, packet) != 0) { success = -1; break; @@ -515,6 +518,48 @@ static void set_ffmpeg_properties(RenderData *rd, } } +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 + * (within a floating point error range). + * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps. + * When converting this to a FFMPEG time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */ + float eps = FLT_EPSILON; + const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; + + /* Calculate the precision of the initial floating point number. */ + if (num > 1.0) { + const uint num_integer_bits = log2_floor_u((unsigned int)num); + + /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) + * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of + * (4-2=2): + * (2) / pow2(23) = floating point precision for 3.5f + */ + eps = (float)(1 << num_integer_bits) * FLT_EPSILON; + } + + /* Calculate how many decimal shifts we can do until we run out of precision. */ + const int max_num_shift = fabsf(log10f(eps)); + /* Calculate how many times we can shift the denominator. */ + const int max_den_shift = log10f(DENUM_MAX) - log10f(den); + const int max_iter = min_ii(max_num_shift, max_den_shift); + + for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) { + /* Increase the number and denominator until both are integers. */ + num *= 10; + den *= 10; + eps *= 10; + } + + AVRational time_base; + time_base.den = den; + time_base.num = (int)num; + + return time_base; +} + /* prepare a video stream for the output file */ static AVStream *alloc_video_stream(FFMpegContext *context, @@ -545,13 +590,24 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_VIDEO; + codec = avcodec_find_encoder(c->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); + /* Get some values from the current render settings */ c->width = rectx; c->height = recty; - /* FIXME: Really bad hack (tm) for NTSC support */ if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + /* FIXME: Really bad hack (tm) for NTSC support */ c->time_base.den = 2997; c->time_base.num = 100; } @@ -559,21 +615,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base.den = rd->frs_sec; c->time_base.num = (int)rd->frs_sec_base; } - else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) { - /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest - * of the world, including FFmpeg). */ - c->time_base.den = (int)(rd->frs_sec * 1000); - c->time_base.num = (int)(rd->frs_sec_base * 1000); - } else { - /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate - * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg. - */ - const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; - const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base; + c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); + } - c->time_base.den = (int)DENUM_MAX; - c->time_base.num = (int)num; + /* As per the time-base documentation here: + * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options + * We want to set the time base to (1 / fps) for fixed frame rate video. + * If it is not possible, we want to set the time-base numbers to something as + * small as possible. + */ + if (c->time_base.num != 1) { + AVRational new_time_base; + if (av_reduce( + &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) { + /* Exact reduction was possible. Use the new value. */ + c->time_base = new_time_base; + } } st->time_base = c->time_base; @@ -585,6 +643,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context, ffmpeg_dict_set_int(&opts, "lossless", 1); } else if (context->ffmpeg_crf >= 0) { + /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when + * encoding with vp9 in crf mode. + * Set this to always be zero for other codecs as well. + * We don't care about bit rate in crf mode. */ + c->bit_rate = 0; ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf); } else { @@ -624,12 +687,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - avcodec_free_context(&c); - return NULL; - } - /* Be sure to use the correct pixel format(e.g. RGB, YUV) */ if (codec->pix_fmts) { @@ -646,12 +703,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); } - if (codec_id == AV_CODEC_ID_H264) { - /* correct wrong default ffmpeg param which crash x264 */ - c->qmin = 10; - c->qmax = 51; - } - /* Keep lossless encodes in the RGB domain. */ if (codec_id == AV_CODEC_ID_HUFFYUV) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { @@ -678,6 +729,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } + /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ + if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) { + c->pix_fmt = AV_PIX_FMT_YUV444P; + } + if (codec_id == AV_CODEC_ID_PNG) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { c->pix_fmt = AV_PIX_FMT_RGBA; @@ -711,10 +767,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->thread_type = FF_THREAD_SLICE; } - if (avcodec_open2(c, codec, &opts) < 0) { + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->video_codec = NULL; return NULL; } av_dict_free(&opts); @@ -774,6 +834,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_AUDIO; + codec = avcodec_find_encoder(c->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); + c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; @@ -803,13 +874,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->sample_fmt = AV_SAMPLE_FMT_FLT; } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - // XXX error("Couldn't find a valid audio codec"); - avcodec_free_context(&c); - return NULL; - } - if (codec->sample_fmts) { /* Check if the preferred sample format for this codec is supported. * this is because, depending on the version of libav, @@ -849,11 +913,14 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, set_ffmpeg_properties(rd, c, "audio", &opts); - if (avcodec_open2(c, codec, &opts) < 0) { - // XXX error("Couldn't initialize audio codec"); + int ret = avcodec_open2(c, codec, &opts); + + 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); @@ -1181,6 +1248,9 @@ static void flush_ffmpeg(FFMpegContext *context) packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif int write_ret = av_interleaved_write_frame(context->outfile, packet); if (write_ret != 0) { @@ -1630,49 +1700,7 @@ static void ffmpeg_set_expert_options(RenderData *rd) IDP_FreePropertyContent(rd->ffcodecdata.properties); } - if (codec_id == AV_CODEC_ID_H264) { - /* - * All options here are for x264, but must be set via ffmpeg. - * The names are therefore different - Search for "x264 to FFmpeg option mapping" - * to get a list. - */ - - /* - * Use CABAC coder. Using "coder:1", which should be equivalent, - * crashes Blender for some reason. Either way - this is no big deal. - */ - BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc"); - - /* - * The other options were taken from the libx264-default.preset - * included in the ffmpeg distribution. - */ - - /* This breaks compatibility for QT. */ - // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop"); - BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma"); - BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "me:hex"); - BKE_ffmpeg_property_add_string(rd, "video", "subq:6"); - BKE_ffmpeg_property_add_string(rd, "video", "me_range:16"); - BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4"); - BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25"); - BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40"); - BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71"); - BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1"); - BKE_ffmpeg_property_add_string(rd, "video", "bf:3"); - BKE_ffmpeg_property_add_string(rd, "video", "refs:2"); - BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6"); - - BKE_ffmpeg_property_add_string(rd, "video", "trellis:0"); - BKE_ffmpeg_property_add_string(rd, "video", "weightb:1"); - BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1"); - BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1"); - BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2"); - } - else if (codec_id == AV_CODEC_ID_DNXHD) { + if (codec_id == AV_CODEC_ID_DNXHD) { if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); } diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index e57a5109a66..3b01bbfb86e 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -22,41 +22,122 @@ namespace blender { -struct Color4f { - float r, g, b, a; +/** + * CPP based color structures. + * + * Strongly typed color storage structures with space and alpha association. + * Will increase readability and visibility of typical mistakes when + * working with colors. + * + * The storage structs can hold 4 channels (r, g, b and a). + * + * Usage: + * + * Convert a theme byte color to a linearrgb premultiplied. + * ``` + * ColorTheme4b theme_color; + * ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color = + * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); + * ``` + * + * The API is structured to make most use of inlining. Most notable are space + * conversions done via `BLI_color_convert_to*` functions. + * + * - Conversions between spaces (theme <=> scene linear) should always be done by + * invoking the `BLI_color_convert_to*` methods. + * - Encoding colors (compressing to store colors inside a less precision storage) + * should be done by invoking the `encode` and `decode` methods. + * - Changing alpha association should be done by invoking `premultiply_alpha` or + * `unpremultiply_alpha` methods. + * + * # Encoding. + * + * Color encoding is used to store colors with less precision as in using `uint8_t` in + * stead of `float`. This encoding is supported for `eSpace::SceneLinear`. + * To make this clear to the developer the `eSpace::SceneLinearByteEncoded` + * space is added. + * + * # Precision + * + * Colors can be stored using `uint8_t` or `float` colors. The conversion + * between the two precisions are available as methods. (`to_4b` and + * `to_4f`). + * + * # Alpha conversion + * + * Alpha conversion is only supported in SceneLinear space. + * + * Extending this file: + * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations + * of rgb based colors. `ColorHsl4f<eSpace::SceneLinear, eAlpha::Premultiplied>` + * - Add non RGB spaces/storages ColorXyz. + */ + +/* Enumeration containing the different alpha modes. */ +enum class eAlpha { + /* Color and alpha are unassociated. */ + Straight, + /* Color and alpha are associated. */ + Premultiplied, +}; +std::ostream &operator<<(std::ostream &stream, const eAlpha &space); - Color4f() = default; +/* Enumeration containing internal spaces. */ +enum class eSpace { + /* Blender theme color space (sRGB). */ + Theme, + /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */ + SceneLinear, + /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ + SceneLinearByteEncoded, +}; +std::ostream &operator<<(std::ostream &stream, const eSpace &space); - Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) +/* Template class to store RGBA values with different precision, space and alpha association. */ +template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA { + public: + ChannelStorageType r, g, b, a; + constexpr ColorRGBA() = default; + + constexpr ColorRGBA(const ChannelStorageType rgba[4]) + : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) { } - Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) + constexpr ColorRGBA(const ChannelStorageType r, + const ChannelStorageType g, + const ChannelStorageType b, + const ChannelStorageType a) + : r(r), g(g), b(b), a(a) { } - operator float *() + operator ChannelStorageType *() { return &r; } - operator const float *() const + operator const ChannelStorageType *() const { return &r; } - friend std::ostream &operator<<(std::ostream &stream, Color4f c) + friend std::ostream &operator<<(std::ostream &stream, + const ColorRGBA<ChannelStorageType, Space, Alpha> &c) { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; + + stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } - friend bool operator==(const Color4f &a, const Color4f &b) + friend bool operator==(const ColorRGBA<ChannelStorageType, Space, Alpha> &a, + const ColorRGBA<ChannelStorageType, Space, Alpha> &b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4f &a, const Color4f &b) + friend bool operator!=(const ColorRGBA<ChannelStorageType, Space, Alpha> &a, + const ColorRGBA<ChannelStorageType, Space, Alpha> &b) { return !(a == b); } @@ -71,58 +152,209 @@ struct Color4f { } }; -struct Color4b { - uint8_t r, g, b, a; +/* Forward declarations of concrete color classes. */ +template<eAlpha Alpha> class ColorSceneLinear4f; +template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b; +template<typename ChannelStorageType> class ColorTheme4; - Color4b() = default; +/* Forward declaration of precision conversion methods. */ +BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b); +BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f); - Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) +template<eAlpha Alpha> +class ColorSceneLinear4f final : public ColorRGBA<float, eSpace::SceneLinear, Alpha> { + public: + constexpr ColorSceneLinear4f<Alpha>() : ColorRGBA<float, eSpace::SceneLinear, Alpha>() { } - Color4b(Color4f other) + constexpr ColorSceneLinear4f<Alpha>(const float *rgba) + : ColorRGBA<float, eSpace::SceneLinear, Alpha>(rgba) { - rgba_float_to_uchar(*this, other); } - operator Color4f() const + constexpr ColorSceneLinear4f<Alpha>(float r, float g, float b, float a) + : ColorRGBA<float, eSpace::SceneLinear, Alpha>(r, g, b, a) { - Color4f result; - rgba_uchar_to_float(result, *this); - return result; } - operator uint8_t *() + /** + * Convert to its byte encoded counter space. + **/ + ColorSceneLinearByteEncoded4b<Alpha> encode() const { - return &r; + ColorSceneLinearByteEncoded4b<Alpha> encoded; + linearrgb_to_srgb_uchar4(encoded, *this); + return encoded; } - operator const uint8_t *() const + /** + * Convert color and alpha association to premultiplied alpha. + * + * Does nothing when color has already a premultiplied alpha. + */ + ColorSceneLinear4f<eAlpha::Premultiplied> premultiply_alpha() const { - return &r; + if constexpr (Alpha == eAlpha::Straight) { + ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied; + straight_to_premul_v4_v4(premultiplied, *this); + return premultiplied; + } + else { + return *this; + } } - friend std::ostream &operator<<(std::ostream &stream, Color4b c) + /** + * Convert color and alpha association to straight alpha. + * + * Does nothing when color has straighten alpha. + */ + ColorSceneLinear4f<eAlpha::Straight> unpremultiply_alpha() const { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; - return stream; + if constexpr (Alpha == eAlpha::Premultiplied) { + ColorSceneLinear4f<eAlpha::Straight> straighten; + premul_to_straight_v4_v4(straighten, *this); + return straighten; + } + else { + return *this; + } } +}; - friend bool operator==(const Color4b &a, const Color4b &b) +template<eAlpha Alpha> +class ColorSceneLinearByteEncoded4b final + : public ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha> { + public: + constexpr ColorSceneLinearByteEncoded4b() = default; + + constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba) + : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(rgba) { - return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4b &a, const Color4b &b) + constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(r, g, b, a) { - return !(a == b); } - uint64_t hash() const + /** + * Convert to back to float color. + */ + ColorSceneLinear4f<Alpha> decode() const + { + ColorSceneLinear4f<Alpha> decoded; + srgb_to_linearrgb_uchar4(decoded, *this); + return decoded; + } +}; + +/** + * Theme color template class. + * + * Don't use directly, but use `ColorTheme4b/ColorTheme4b`. + * + * This has been implemented as a template to improve inlining. When implemented as concrete + * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be + * inlined. + */ +template<typename ChannelStorageType> +class ColorTheme4 final : public ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight> { + public: + constexpr ColorTheme4() : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(){}; + + constexpr ColorTheme4(const ChannelStorageType *rgba) + : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(rgba) + { + } + + constexpr ColorTheme4(ChannelStorageType r, + ChannelStorageType g, + ChannelStorageType b, + ChannelStorageType a) + : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(r, g, b, a) + { + } + + /** + * Change precision of color to float. + */ + ColorTheme4<float> to_4f() const { - return static_cast<uint64_t>(r * 1283591) ^ static_cast<uint64_t>(g * 850177) ^ - static_cast<uint64_t>(b * 735391) ^ static_cast<uint64_t>(a * 442319); + if constexpr ((std::is_same_v<ChannelStorageType, uint8_t>)) { + return BLI_color_convert_to_theme4f(*this); + } + else { + return *this; + } + } + + /** + * Change precision of color to uint8_t. + */ + ColorTheme4<uint8_t> to_4b() const + { + if constexpr ((std::is_same_v<ChannelStorageType, float>)) { + return BLI_color_convert_to_theme4b(*this); + } + else { + return *this; + } } }; +using ColorTheme4b = ColorTheme4<uint8_t>; +using ColorTheme4f = ColorTheme4<float>; + +BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f) +{ + ColorTheme4b theme4b; + rgba_float_to_uchar(theme4b, theme4f); + return theme4b; +} + +BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b) +{ + ColorTheme4f theme4f; + rgba_uchar_to_float(theme4f, theme4b); + return theme4f; +} + +BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear( + const ColorTheme4f &theme4f) +{ + ColorSceneLinear4f<eAlpha::Straight> scene_linear; + srgb_to_linearrgb_v4(scene_linear, theme4f); + return scene_linear; +} + +BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear( + const ColorTheme4b &theme4b) +{ + ColorSceneLinear4f<eAlpha::Straight> scene_linear; + srgb_to_linearrgb_uchar4(scene_linear, theme4b); + return scene_linear; +} + +BLI_INLINE ColorTheme4f +BLI_color_convert_to_theme4f(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear) +{ + ColorTheme4f theme4f; + linearrgb_to_srgb_v4(theme4f, scene_linear); + return theme4f; +} + +BLI_INLINE ColorTheme4b +BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear) +{ + ColorTheme4b theme4b; + linearrgb_to_srgb_uchar4(theme4b, scene_linear); + return theme4b; +} + +/* Internal roles. For convenience to shorten the type names and hide complexity. */ +using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>; +using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>; + } // namespace blender diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index 48b01edcd6f..f04c0e9c80a 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -110,7 +110,7 @@ class IndexMask { } /** - * Returns the n-th index referenced by this IndexMask. The `index_mask` method returns an + * Returns the n-th index referenced by this IndexMask. The `index_range` method returns an * IndexRange containing all indices that can be used as parameter here. */ int64_t operator[](int64_t n) const diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 028ca31a059..46219ad5493 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -154,6 +154,9 @@ MINLINE int max_iii(int a, int b, int c); MINLINE int min_iiii(int a, int b, int c, int d); MINLINE int max_iiii(int a, int b, int c, int d); +MINLINE uint min_uu(uint a, uint b); +MINLINE uint max_uu(uint a, uint b); + MINLINE size_t min_zz(size_t a, size_t b); MINLINE size_t max_zz(size_t a, size_t b); diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh index fb5e2b61cdb..b9eda2ad7e1 100644 --- a/source/blender/blenlib/BLI_mpq3.hh +++ b/source/blender/blenlib/BLI_mpq3.hh @@ -218,6 +218,15 @@ struct mpq3 { return a.x * b.x + a.y * b.y + a.z * b.z; } + static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer *= b; + buffer.x += buffer.y; + buffer.x += buffer.z; + return buffer.x; + } + static mpq3 cross(const mpq3 &a, const mpq3 &b) { return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); @@ -246,6 +255,13 @@ struct mpq3 { return mpq3::dot(diff, diff); } + static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer -= b; + return mpq3::dot(buffer, buffer); + } + static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t) { mpq_class s = 1 - t; diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 8bea2584ca7..f6f546c90b9 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -161,7 +161,7 @@ class Vector { } /** - * Create a vector from an array ref. The values in the vector are copy constructed. + * Create a vector from a span. The values in the vector are copy constructed. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator) diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index ce3515ac153..f3dc343ee20 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC intern/BLI_args.c intern/BLI_array.c intern/BLI_assert.c + intern/BLI_color.cc intern/BLI_dial_2d.c intern/BLI_dynstr.c intern/BLI_filelist.c @@ -389,6 +390,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc tests/BLI_edgehash_test.cc diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc new file mode 100644 index 00000000000..6dcef4f4688 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_color.cc @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include "BLI_color.hh" + +namespace blender { + +std::ostream &operator<<(std::ostream &stream, const eAlpha &space) +{ + switch (space) { + case eAlpha::Straight: { + stream << "Straight"; + break; + } + case eAlpha::Premultiplied: { + stream << "Premultiplied"; + break; + } + } + return stream; +} + +std::ostream &operator<<(std::ostream &stream, const eSpace &space) +{ + switch (space) { + case eSpace::Theme: { + stream << "Theme"; + break; + } + case eSpace::SceneLinear: { + stream << "SceneLinear"; + break; + } + case eSpace::SceneLinearByteEncoded: { + stream << "SceneLinearByteEncoded"; + break; + } + } + return stream; +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 106bd5bc793..107c27da6a2 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos( z_stream strm; unsigned char out[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g size_t chunk = 256 * 1024; unsigned char in[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 2a7c091d1b9..d73afff64c8 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -514,6 +514,15 @@ MINLINE int max_ii(int a, int b) return (b < a) ? a : b; } +MINLINE uint min_uu(uint a, uint b) +{ + return (a < b) ? a : b; +} +MINLINE uint max_uu(uint a, uint b) +{ + return (b < a) ? a : b; +} + MINLINE float min_fff(float a, float b, float c) { return min_ff(min_ff(a, b), c); @@ -798,9 +807,9 @@ MINLINE unsigned char unit_float_to_uchar_clamp(float val) MINLINE unsigned short unit_float_to_ushort_clamp(float val) { - return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? - 65535 : - (val <= 0.0f) ? 0 : (val * 65535.0f + 0.5f)); + return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? 65535 : + (val <= 0.0f) ? 0 : + (val * 65535.0f + 0.5f)); } #define unit_float_to_ushort_clamp(val) \ ((CHECK_TYPE_INLINE(val, float)), unit_float_to_ushort_clamp(val)) diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c index d477c20a242..5ca7b96c136 100644 --- a/source/blender/blenlib/intern/memory_utils.c +++ b/source/blender/blenlib/intern/memory_utils.c @@ -31,7 +31,7 @@ #include "BLI_strict_flags.h" /** - * Check if memory is zero'd, as with memset(arr, 0, arr_size) + * Check if memory is zeroed, as with `memset(arr, 0, arr_size)`. */ bool BLI_memory_is_zero(const void *arr, const size_t arr_size) { diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 25291b8c3b0..00d53a010b0 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -41,6 +41,7 @@ # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_stack.hh" +# include "BLI_task.hh" # include "BLI_vector.hh" # include "BLI_vector_set.hh" @@ -48,6 +49,10 @@ # include "BLI_mesh_boolean.hh" +# ifdef WITH_TBB +# include "tbb/spin_mutex.h" +# endif + // # define PERFDEBUG namespace blender::meshintersect { @@ -144,11 +149,9 @@ class TriMeshTopology : NonCopyable { * Else return NO_INDEX. */ int other_tri_if_manifold(Edge e, int t) const { - if (edge_tri_.contains(e)) { - auto *p = edge_tri_.lookup(e); - if (p->size() == 2) { - return ((*p)[0] == t) ? (*p)[1] : (*p)[0]; - } + auto p = edge_tri_.lookup_ptr(e); + if (p != nullptr && (*p)->size() == 2) { + return ((**p)[0] == t) ? (**p)[1] : (**p)[0]; } return NO_INDEX; } @@ -1690,9 +1693,24 @@ static int find_containing_cell(const Vert *v, * If the closest point is on an edge, return 0, 1, or 2 * for edges ab, bc, or ca in *r_edge; else -1. * (Adapted from #closest_on_tri_to_point_v3()). + * The arguments ab, ac, ..., r are used as temporaries + * in this routine. Passing them in from the caller can + * avoid many allocs and frees of temporary mpq3 values + * and the mpq_class values within them. */ -static mpq_class closest_on_tri_to_point( - const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert) +static mpq_class closest_on_tri_to_point(const mpq3 &p, + const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + mpq3 &ab, + mpq3 &ac, + mpq3 &ap, + mpq3 &bp, + mpq3 &cp, + mpq3 &m, + mpq3 &r, + int *r_edge, + int *r_vert) { constexpr int dbg_level = 0; if (dbg_level > 0) { @@ -1700,11 +1718,15 @@ static mpq_class closest_on_tri_to_point( std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n"; } /* Check if p in vertex region outside a. */ - mpq3 ab = b - a; - mpq3 ac = c - a; - mpq3 ap = p - a; - mpq_class d1 = mpq3::dot(ab, ap); - mpq_class d2 = mpq3::dot(ac, ap); + ab = b; + ab -= a; + ac = c; + ac -= a; + ap = p; + ap -= a; + + mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m); + mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m); if (d1 <= 0 && d2 <= 0) { /* Barycentric coordinates (1,0,0). */ *r_edge = -1; @@ -1712,12 +1734,13 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = a\n"; } - return mpq3::distance_squared(p, a); + return mpq3::distance_squared_with_buffer(p, a, m); } /* Check if p in vertex region outside b. */ - mpq3 bp = p - b; - mpq_class d3 = mpq3::dot(ab, bp); - mpq_class d4 = mpq3::dot(ac, bp); + bp = p; + bp -= b; + mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m); + mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m); if (d3 >= 0 && d4 <= d3) { /* Barycentric coordinates (0,1,0). */ *r_edge = -1; @@ -1725,25 +1748,28 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = b\n"; } - return mpq3::distance_squared(p, b); + return mpq3::distance_squared_with_buffer(p, b, m); } /* Check if p in region of ab. */ mpq_class vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { mpq_class v = d1 / (d1 - d3); /* Barycentric coordinates (1-v,v,0). */ - mpq3 r = a + v * ab; + r = ab; + r *= v; + r += a; *r_vert = -1; *r_edge = 0; if (dbg_level > 0) { std::cout << " answer = on ab at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in vertex region outside c. */ - mpq3 cp = p - c; - mpq_class d5 = mpq3::dot(ab, cp); - mpq_class d6 = mpq3::dot(ac, cp); + cp = p; + cp -= c; + mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m); + mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m); if (d6 >= 0 && d5 <= d6) { /* Barycentric coordinates (0,0,1). */ *r_edge = -1; @@ -1751,49 +1777,67 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = c\n"; } - return mpq3::distance_squared(p, c); + return mpq3::distance_squared_with_buffer(p, c, m); } /* Check if p in edge region of ac. */ mpq_class vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { mpq_class w = d2 / (d2 - d6); /* Barycentric coordinates (1-w,0,w). */ - mpq3 r = a + w * ac; + r = ac; + r *= w; + r += a; *r_vert = -1; *r_edge = 2; if (dbg_level > 0) { std::cout << " answer = on ac at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in edge region of bc. */ mpq_class va = d3 * d6 - d5 * d4; if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); /* Barycentric coordinates (0,1-w,w). */ - mpq3 r = c - b; - r = w * r; - r = r + b; + r = c; + r -= b; + r *= w; + r += b; *r_vert = -1; *r_edge = 1; if (dbg_level > 0) { std::cout << " answer = on bc at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* p inside face region. Compute barycentric coordinates (u,v,w). */ mpq_class denom = 1 / (va + vb + vc); mpq_class v = vb * denom; mpq_class w = vc * denom; - ac = w * ac; - mpq3 r = a + v * ab; - r = r + ac; + ac *= w; + r = ab; + r *= v; + r += a; + r += ac; *r_vert = -1; *r_edge = -1; if (dbg_level > 0) { std::cout << " answer = inside at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); +} + +static float closest_on_tri_to_point_float_dist_squared(const float3 &p, + const double3 &a, + const double3 &b, + const double3 &c) +{ + float3 fa, fb, fc, closest; + copy_v3fl_v3db(fa, a); + copy_v3fl_v3db(fb, b); + copy_v3fl_v3db(fc, c); + closest_on_tri_to_point_v3(closest, p, fa, fb, fc); + return len_squared_v3v3(p, closest); } struct ComponentContainer { @@ -1832,6 +1876,11 @@ static Vector<ComponentContainer> find_component_containers(int comp, if (dbg_level > 0) { std::cout << "test vertex in comp: " << test_v << "\n"; } + const double3 &test_v_d = test_v->co; + float3 test_v_f(test_v_d[0], test_v_d[1], test_v_d[2]); + + mpq3 buf[7]; + for (int comp_other : components.index_range()) { if (comp == comp_other) { continue; @@ -1843,6 +1892,7 @@ static Vector<ComponentContainer> find_component_containers(int comp, int nearest_tri_close_vert = -1; int nearest_tri_close_edge = -1; mpq_class nearest_tri_dist_squared; + float nearest_tri_dist_squared_float = FLT_MAX; for (int p : components[comp_other]) { const Patch &patch = pinfo.patch(p); for (int t : patch.tris()) { @@ -1852,10 +1902,23 @@ static Vector<ComponentContainer> find_component_containers(int comp, } int close_vert; int close_edge; + /* Try a cheap float test first. */ + float d2_f = closest_on_tri_to_point_float_dist_squared( + test_v_f, tri[0]->co, tri[1]->co, tri[2]->co); + if (d2_f - FLT_EPSILON > nearest_tri_dist_squared_float) { + continue; + } mpq_class d2 = closest_on_tri_to_point(test_v->co_exact, tri[0]->co_exact, tri[1]->co_exact, tri[2]->co_exact, + buf[0], + buf[1], + buf[2], + buf[3], + buf[4], + buf[5], + buf[6], &close_edge, &close_vert); if (dbg_level > 1) { @@ -1867,6 +1930,7 @@ static Vector<ComponentContainer> find_component_containers(int comp, nearest_tri_close_edge = close_edge; nearest_tri_close_vert = close_vert; nearest_tri_dist_squared = d2; + nearest_tri_dist_squared_float = d2_f; } } } @@ -2567,47 +2631,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm, BVHTree *tree = raycast_tree(tm); Vector<Face *> out_faces; out_faces.reserve(tm.face_size()); - Array<float> in_shape(nshapes, 0); - Array<int> winding(nshapes, 0); - for (int t : tm.face_index_range()) { - Face &tri = *tm.face(t); - int shape = shape_fn(tri.orig); - if (dbg_level > 0) { - std::cout << "process triangle " << t << " = " << &tri << "\n"; - std::cout << "shape = " << shape << "\n"; - } - test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); - for (int other_shape = 0; other_shape < nshapes; ++other_shape) { - if (other_shape == shape) { - continue; - } - /* The in_shape array has a confidence value for "insideness". - * For most operations, even a hint of being inside - * gives good results, but when shape is a cutter in a Difference - * operation, we want to be pretty sure that the point is inside other_shape. - * E.g., T75827. - * Also, when the operation is intersection, we also want high confidence. - */ - bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || - op == BoolOpType::Intersect; - bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); +# ifdef WITH_TBB + tbb::spin_mutex mtx; +# endif + const int grainsize = 256; + parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) { + Array<float> in_shape(nshapes, 0); + Array<int> winding(nshapes, 0); + for (int t : range) { + Face &tri = *tm.face(t); + int shape = shape_fn(tri.orig); if (dbg_level > 0) { - std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " - << other_shape << " val = " << in_shape[other_shape] << "\n"; + std::cout << "process triangle " << t << " = " << &tri << "\n"; + std::cout << "shape = " << shape << "\n"; } - winding[other_shape] = inside; - } - bool do_flip; - bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); - if (!do_remove) { - if (!do_flip) { - out_faces.append(&tri); + test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); + for (int other_shape = 0; other_shape < nshapes; ++other_shape) { + if (other_shape == shape) { + continue; + } + /* The in_shape array has a confidence value for "insideness". + * For most operations, even a hint of being inside + * gives good results, but when shape is a cutter in a Difference + * operation, we want to be pretty sure that the point is inside other_shape. + * E.g., T75827. + * Also, when the operation is intersection, we also want high confidence. + */ + bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || + op == BoolOpType::Intersect; + bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " + << other_shape << " val = " << in_shape[other_shape] << "\n"; + } + winding[other_shape] = inside; } - else { - raycast_add_flipped(out_faces, tri, arena); + bool do_flip; + bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); + { +# ifdef WITH_TBB + tbb::spin_mutex::scoped_lock lock(mtx); +# endif + if (!do_remove) { + if (!do_flip) { + out_faces.append(&tri); + } + else { + raycast_add_flipped(out_faces, tri, arena); + } + } } } - } + }); BLI_bvhtree_free(tree); ans.set_faces(out_faces); return ans; @@ -3259,9 +3334,13 @@ static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out, std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n"; } /* For now: need plane normals for all triangles. */ - for (Face *tri : tm_out.faces()) { - tri->populate_plane(false); - } + const int grainsize = 1024; + parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) { + for (int i : range) { + Face *tri = tm_out.face(i); + tri->populate_plane(false); + } + }); /* Gather all output triangles that are part of each input face. * face_output_tris[f] will be indices of triangles in tm_out * that have f as their original face. */ diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 636209883c3..97f856476c5 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1165,13 +1165,19 @@ static int filter_plane_side(const double3 &p, * Assumes ab is not perpendicular to n. * This works because the ratio of the projections of ab and ac onto n is the same as * the ratio along the line ab of the intersection point to the whole of ab. + * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n) -{ - mpq3 ab = a - b; - mpq_class den = mpq3::dot(ab, n); +static inline mpq3 tti_interp( + const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf) +{ + ab = a; + ab -= b; + ac = a; + ac -= c; + mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf); BLI_assert(den != 0); - mpq_class alpha = mpq3::dot(a - c, n) / den; + mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den; return a - alpha * ab; } @@ -1179,11 +1185,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations. * TODO: change arguments to `const Vert *` and use floating filters. + * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad) +static inline int tti_above(const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + const mpq3 &ad, + mpq3 &ba, + mpq3 &ca, + mpq3 &n, + mpq3 &dotbuf) { - mpq3 n = mpq3::cross(b - a, c - a); - return sgn(mpq3::dot(ad, n)); + ba = b; + ba -= a; + ca = c; + ca -= a; + + n.x = ba.y * ca.z - ba.z * ca.y; + n.y = ba.z * ca.x - ba.x * ca.z; + n.z = ba.x * ca.y - ba.y * ca.x; + + return sgn(mpq3::dot_with_buffer(ad, n, dotbuf)); } /** @@ -1227,20 +1250,21 @@ static ITT_value itt_canon2(const mpq3 &p1, mpq3 p1p2 = p2 - p1; mpq3 intersect_1; mpq3 intersect_2; + mpq3 buf[4]; bool no_overlap = false; /* Top test in classification tree. */ - if (tti_above(p1, q1, r2, p1p2) > 0) { + if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Middle right test in classification tree. */ - if (tti_above(p1, r1, r2, p1p2) <= 0) { + if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) { /* Bottom right test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) > 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Overlap is [k [i l] j]. */ if (dbg_level > 0) { std::cout << "overlap [k [i l] j]\n"; } /* i is intersect with p1r1. l is intersect with p2r2. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k l] j]. */ @@ -1248,8 +1272,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k l] j]\n"; } /* k is intersect with p2q2. l is intersect is p2r2. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } } else { @@ -1262,7 +1286,7 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Middle left test in classification tree. */ - if (tti_above(p1, q1, q2, p1p2) < 0) { + if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) { /* No overlap: [i j] [k l]. */ if (dbg_level > 0) { std::cout << "no overlap: [i j] [k l]\n"; @@ -1271,14 +1295,14 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Bottom left test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) >= 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) { /* Overlap is [k [i j] l]. */ if (dbg_level > 0) { std::cout << "overlap [k [i j] l]\n"; } /* i is intersect with p1r1. j is intersect with p1q1. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k j] l]. */ @@ -1286,8 +1310,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k j] l]\n"; } /* k is intersect with p2q2. j is intersect with p1q1. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } } } @@ -1438,6 +1462,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) return ITT_value(INONE); } + mpq3 buf[2]; const mpq3 &p1 = vp1->co_exact; const mpq3 &q1 = vq1->co_exact; const mpq3 &r1 = vr1->co_exact; @@ -1447,13 +1472,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) const mpq3 &n2 = tri2.plane->norm_exact; if (sp1 == 0) { - sp1 = sgn(mpq3::dot(p1 - r2, n2)); + buf[0] = p1; + buf[0] -= r2; + sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sq1 == 0) { - sq1 = sgn(mpq3::dot(q1 - r2, n2)); + buf[0] = q1; + buf[0] -= r2; + sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sr1 == 0) { - sr1 = sgn(mpq3::dot(r1 - r2, n2)); + buf[0] = r1; + buf[0] -= r2; + sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (dbg_level > 1) { @@ -1473,13 +1504,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) /* Repeat for signs of t2's vertices with respect to plane of t1. */ const mpq3 &n1 = tri1.plane->norm_exact; if (sp2 == 0) { - sp2 = sgn(mpq3::dot(p2 - r1, n1)); + buf[0] = p2; + buf[0] -= r1; + sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sq2 == 0) { - sq2 = sgn(mpq3::dot(q2 - r1, n1)); + buf[0] = q2; + buf[0] -= r1; + sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sr2 == 0) { - sr2 = sgn(mpq3::dot(r2 - r1, n1)); + buf[0] = r2; + buf[0] -= r1; + sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (dbg_level > 1) { diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c index 7bfca149ffb..98fa5c872b0 100644 --- a/source/blender/blenlib/intern/polyfill_2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c @@ -375,7 +375,7 @@ void BLI_polyfill_beautify(const float (*coords)[2], for (uint i = 0, base_index = 0; i < order_edges_len; base_index++) { const struct OrderEdge *oe_a = &order_edges[i++]; const struct OrderEdge *oe_b = &order_edges[i++]; - BLI_assert(oe_a->verts[0] == oe_a->verts[0] && oe_a->verts[1] == oe_a->verts[1]); + BLI_assert(oe_a->verts[0] == oe_b->verts[0] && oe_a->verts[1] == oe_b->verts[1]); half_edges[oe_a->e_half].e_radial = oe_b->e_half; half_edges[oe_b->e_half].e_radial = oe_a->e_half; half_edges[oe_a->e_half].base_index = base_index; diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c index 9586da941a4..7d7436411ac 100644 --- a/source/blender/blenlib/intern/timecode.c +++ b/source/blender/blenlib/intern/timecode.c @@ -216,10 +216,10 @@ size_t BLI_timecode_string_from_time_simple(char *str, const int hun = ((int)(fmod(time_seconds, 1.0) * 100)); if (hr) { - rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun); + rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun); } else { - rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun); + rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun); } return rlen; diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index 333b6783087..3aa61d1fec5 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -94,9 +94,9 @@ void BLI_windows_register_blend_extension(const bool background) GetModuleFileName(0, BlPath, MAX_PATH); /* Replace the actual app name with the wrapper. */ - blender_app = strstr(BlPath, "blender-app.exe"); + blender_app = strstr(BlPath, "blender.exe"); if (blender_app != NULL) { - strcpy(blender_app, "blender.exe"); + strcpy(blender_app, "blender-launcher.exe"); } /* root is HKLM by default */ diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc new file mode 100644 index 00000000000..14796e6bf71 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_color_test.cc @@ -0,0 +1,133 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_color.hh" + +namespace blender::tests { + +/** + * \name Conversions + * \{ */ + +TEST(color, ThemeByteToFloat) +{ + ColorTheme4b theme_byte(192, 128, 64, 128); + ColorTheme4f theme_float = theme_byte.to_4f(); + EXPECT_NEAR(0.75f, theme_float.r, 0.01f); + EXPECT_NEAR(0.5f, theme_float.g, 0.01f); + EXPECT_NEAR(0.25f, theme_float.b, 0.01f); + EXPECT_NEAR(0.5f, theme_float.a, 0.01f); +} + +TEST(color, SrgbStraightFloatToByte) +{ + ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme_byte = theme_float.to_4b(); + EXPECT_EQ(191, theme_byte.r); + EXPECT_EQ(128, theme_byte.g); + EXPECT_EQ(64, theme_byte.b); + EXPECT_EQ(128, theme_byte.a); +} + +TEST(color, SrgbStraightToSceneLinearPremultiplied) +{ + BLI_init_srgb_conversion(); + + ColorTheme4b theme(192, 128, 64, 128); + ColorSceneLinear4f<eAlpha::Premultiplied> linear = + BLI_color_convert_to_scene_linear(theme).premultiply_alpha(); + EXPECT_NEAR(0.26f, linear.r, 0.01f); + EXPECT_NEAR(0.11f, linear.g, 0.01f); + EXPECT_NEAR(0.02f, linear.b, 0.01f); + EXPECT_NEAR(0.5f, linear.a, 0.01f); +} + +TEST(color, SceneLinearStraightToPremultiplied) +{ + ColorSceneLinear4f<eAlpha::Straight> straight(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied = straight.premultiply_alpha(); + EXPECT_NEAR(0.37f, premultiplied.r, 0.01f); + EXPECT_NEAR(0.25f, premultiplied.g, 0.01f); + EXPECT_NEAR(0.12f, premultiplied.b, 0.01f); + EXPECT_NEAR(0.5f, premultiplied.a, 0.01f); +} + +TEST(color, SceneLinearPremultipliedToStraight) +{ + ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f<eAlpha::Straight> straight = premultiplied.unpremultiply_alpha(); + EXPECT_NEAR(1.5f, straight.r, 0.01f); + EXPECT_NEAR(1.0f, straight.g, 0.01f); + EXPECT_NEAR(0.5f, straight.b, 0.01f); + EXPECT_NEAR(0.5f, straight.a, 0.01f); +} + +TEST(color, SceneLinearStraightSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear); + EXPECT_NEAR(0.88f, theme.r, 0.01); + EXPECT_NEAR(0.73f, theme.g, 0.01); + EXPECT_NEAR(0.53f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearPremultipliedToSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha()); + + EXPECT_NEAR(1.19f, theme.r, 0.01); + EXPECT_NEAR(1.0f, theme.g, 0.01); + EXPECT_NEAR(0.74f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearStraightSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear); + EXPECT_EQ(225, theme.r); + EXPECT_EQ(188, theme.g); + EXPECT_EQ(137, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearPremultipliedToSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha()); + EXPECT_EQ(255, theme.r); + EXPECT_EQ(255, theme.g); + EXPECT_EQ(188, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearByteEncoding) +{ + ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded = linear.encode(); + EXPECT_EQ(225, encoded.r); + EXPECT_EQ(188, encoded.g); + EXPECT_EQ(137, encoded.b); + EXPECT_EQ(128, encoded.a); +} + +TEST(color, SceneLinearByteDecoding) +{ + ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded(225, 188, 137, 128); + ColorSceneLinear4f<eAlpha::Premultiplied> decoded = encoded.decode(); + EXPECT_NEAR(0.75f, decoded.r, 0.01f); + EXPECT_NEAR(0.5f, decoded.g, 0.01f); + EXPECT_NEAR(0.25f, decoded.b, 0.01f); + EXPECT_NEAR(0.5f, decoded.a, 0.01f); +} + +/* \} */ + +} // namespace blender::tests diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index fe7d50bfa15..801f5864c8d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4452,7 +4452,9 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) if (id == NULL) { /* ID has not been read yet, add placeholder to the main of the * library it belongs to, so that it will be read later. */ - read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, NULL); + read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, &id); + id_sort_by_name(which_libbase(libmain, GS(id->name)), id, id->prev); + /* commented because this can print way too much */ // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->filepath); @@ -4512,7 +4514,8 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) bhead, fd->id_tag_extra | LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, false, - NULL); + &id); + id_sort_by_name(which_libbase(mainvar, GS(id->name)), id, id->prev); } else { /* Convert any previously read weak link to regular link diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6338dc95aba..990fc1d65d7 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name) id->flag = LIB_FAKEUSER; *((short *)id->name) = ID_GD; - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); /* alphabetic insertion: is in BKE_id_new_name_validate */ if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8c5e86eadd3..284a30a280d 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,10 +22,12 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" #include "DNA_genfile.h" +#include "DNA_listBase.h" #include "DNA_modifier_types.h" #include "DNA_text_types.h" @@ -36,6 +38,43 @@ #include "BLO_readfile.h" #include "readfile.h" +static void sort_linked_ids(Main *bmain) +{ + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ListBase temp_list; + BLI_listbase_clear(&temp_list); + LISTBASE_FOREACH_MUTABLE (ID *, id, lb) { + if (ID_IS_LINKED(id)) { + BLI_remlink(lb, id); + BLI_addtail(&temp_list, id); + id_sort_by_name(&temp_list, id, NULL); + } + } + BLI_movelisttolist(lb, &temp_list); + } + FOREACH_MAIN_LISTBASE_END; +} + +static void assert_sorted_ids(Main *bmain) +{ +#ifndef NDEBUG + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ID *id_prev = NULL; + LISTBASE_FOREACH (ID *, id, lb) { + if (id_prev == NULL) { + continue; + } + BLI_assert(id_prev->lib != id->lib || BLI_strcasecmp(id_prev->name, id->name) < 0); + } + } + FOREACH_MAIN_LISTBASE_END; +#else + UNUSED_VARS_NDEBUG(bmain); +#endif +} + void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { @@ -46,19 +85,8 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_300 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 3)) { /* Use new texture socket in Attribute Sample Texture node. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { @@ -82,9 +110,55 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) node->id = NULL; } } + + sort_linked_ids(bmain); + assert_sorted_ids(bmain); + } + if (MAIN_VERSION_ATLEAST(bmain, 300, 3)) { + assert_sorted_ids(bmain); + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_300 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } +static void version_switch_node_input_prefix(Main *bmain) +{ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_SWITCH) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + /* Skip the "switch" socket. */ + if (socket == node->inputs.first) { + continue; + } + strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True"); + + /* Replace "A" and "B", but keep the unique number suffix at the end. */ + char number_suffix[8]; + BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix)); + strcpy(socket->identifier, socket->name); + strcat(socket->identifier, number_suffix); + } + } + } + } + } + FOREACH_NODETREE_END; +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -110,6 +184,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) { + version_switch_node_input_prefix(bmain); + + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose == NULL) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -121,15 +211,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ - if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->pose == NULL) { - continue; - } - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); - } - } - } } } diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index c215cf69e3a..4bd88ae62d7 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -102,6 +102,10 @@ set(SRC intern/bmesh_mesh_convert.h intern/bmesh_mesh_duplicate.c intern/bmesh_mesh_duplicate.h + intern/bmesh_mesh_tessellate.c + intern/bmesh_mesh_tessellate.h + intern/bmesh_mesh_partial_update.c + intern/bmesh_mesh_partial_update.h intern/bmesh_mesh_validate.c intern/bmesh_mesh_validate.h intern/bmesh_mods.c @@ -182,10 +186,6 @@ set(LIB extern_rangetree ) -if(MSVC AND NOT MSVC_CLANG) - string(APPEND CMAKE_C_FLAGS " /WX /wd4101") -endif() - if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} @@ -225,6 +225,10 @@ endif() blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +if(MSVC AND NOT MSVC_CLANG) + target_compile_options(bf_bmesh PRIVATE /WX /wd4101) +endif() + if(WITH_GTESTS) set(TEST_SRC tests/bmesh_core_test.cc diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 4441ccc0c88..d8c3f4ce556 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -215,6 +215,8 @@ extern "C" { #include "intern/bmesh_mesh.h" #include "intern/bmesh_mesh_convert.h" #include "intern/bmesh_mesh_duplicate.h" +#include "intern/bmesh_mesh_partial_update.h" +#include "intern/bmesh_mesh_tessellate.h" #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" #include "intern/bmesh_operators.h" diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 5e879d41d43..d4357fa561b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -373,15 +373,16 @@ typedef struct BMVertsCalcNormalsData { float (*vnos)[3]; } BMVertsCalcNormalsData; -static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f) +static void mesh_verts_calc_normals_accum( + BMFace *f, + const float *f_no, + const float (*edgevec)[3], + + /* Read-write data, protected by an atomic-based fake spin-lock like system. */ + float (*vnos)[3]) { #define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) - BMVertsCalcNormalsData *data = userdata; - BMFace *f = (BMFace *)mp_f; - - const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no; - BMLoop *l_first, *l_iter; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { @@ -391,8 +392,8 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp /* calculate the dot product of the two edges that * meet at the loop's vertex */ - e1diff = data->edgevec[BM_elem_index_get(l_iter->prev->e)]; - e2diff = data->edgevec[BM_elem_index_get(l_iter->e)]; + e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)]; + e2diff = edgevec[BM_elem_index_get(l_iter->e)]; dotprod = dot_v3v3(e1diff, e2diff); /* edge vectors are calculated from e->v1 to e->v2, so @@ -410,7 +411,7 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp } /* accumulate weighted face normal into the vertex's normal */ - float *v_no = data->vnos ? data->vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no; + float *v_no = vnos ? vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no; /* This block is a lockless threadsafe madd_v3_v3fl. * It uses the first float of the vector as a sort of cheap spin-lock, @@ -447,6 +448,14 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp #undef FLT_EQ_NONAN } +static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f) +{ + BMVertsCalcNormalsData *data = userdata; + BMFace *f = (BMFace *)mp_f; + const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no; + mesh_verts_calc_normals_accum(f, f_no, data->edgevec, data->vnos); +} + static void mesh_verts_calc_normals_normalize_cb(void *userdata, MempoolIterData *mp_v) { BMVertsCalcNormalsData *data = userdata; @@ -529,6 +538,130 @@ void BM_mesh_normals_update(BMesh *bm) MEM_freeN(edgevec); } +static void mesh_faces_parallel_range_calc_normals_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMFace *f = ((BMFace **)userdata)[iter]; + BM_face_normal_update(f); +} + +static void mesh_edges_parallel_range_calc_vectors_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMEdge *e = ((BMEdge **)((void **)userdata)[0])[iter]; + float *r_edgevec = ((float(*)[3])((void **)userdata)[1])[iter]; + sub_v3_v3v3(r_edgevec, e->v1->co, e->v2->co); + normalize_v3(r_edgevec); +} + +static void mesh_verts_parallel_range_calc_normals_accum_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMFace *f = ((BMFace **)((void **)userdata)[0])[iter]; + const float(*edgevec)[3] = (float(*)[3])((void **)userdata)[1]; + mesh_verts_calc_normals_accum(f, f->no, edgevec, NULL); +} + +static void mesh_verts_parallel_range_calc_normals_normalize_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMVert *v = ((BMVert **)userdata)[iter]; + if (UNLIKELY(normalize_v3(v->no) == 0.0f)) { + normalize_v3_v3(v->no, v->co); + } +} + +/** + * A version of #BM_mesh_normals_update that updates a subset of geometry, + * used to avoid the overhead of updating everything. + */ +void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo) +{ + BLI_assert(bmpinfo->params.do_normals); + + BMVert **verts = bmpinfo->verts; + BMEdge **edges = bmpinfo->edges; + BMFace **faces = bmpinfo->faces; + const int verts_len = bmpinfo->verts_len; + const int edges_len = bmpinfo->edges_len; + const int faces_len = bmpinfo->faces_len; + const int faces_len_normal_calc_accumulate = bmpinfo->faces_len_normal_calc_accumulate; + + float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__); + + for (int i = 0; i < verts_len; i++) { + zero_v3(verts[i]->no); + } + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + { + /* Faces. */ + BLI_task_parallel_range( + 0, faces_len, faces, mesh_faces_parallel_range_calc_normals_cb, &settings); + } + + /* Temporarily override the edge indices, + * storing the correct indices in the case they're not dirty. + * + * \note in most cases indices are modified and #BMesh.elem_index_dirty is set. + * This is an exceptional case where indices are restored because the worst case downside + * of marking the edge indices dirty would require a full loop over all edges to + * correct the indices in other functions which need them to be valid. + * When moving a few vertices on a high poly mesh setting and restoring connected + * edges has very little overhead compared with restoring all edge indices. */ + int *edge_index_value = NULL; + if ((bm->elem_index_dirty & BM_EDGE) == 0) { + edge_index_value = MEM_mallocN(sizeof(*edge_index_value) * edges_len, __func__); + + for (int i = 0; i < edges_len; i++) { + BMEdge *e = edges[i]; + edge_index_value[i] = BM_elem_index_get(e); + BM_elem_index_set(e, i); /* set_dirty! (restore before this function exits). */ + } + } + else { + for (int i = 0; i < edges_len; i++) { + BMEdge *e = edges[i]; + BM_elem_index_set(e, i); /* set_dirty! (already dirty) */ + } + } + + { + /* Verts. */ + + /* Compute normalized direction vectors for each edge. + * Directions will be used for calculating the weights of the face normals on the vertex + * normals. */ + void *data[2] = {edges, edgevec}; + BLI_task_parallel_range( + 0, edges_len, data, mesh_edges_parallel_range_calc_vectors_cb, &settings); + + /* Add weighted face normals to vertices. */ + data[0] = faces; + BLI_task_parallel_range(0, + faces_len_normal_calc_accumulate, + data, + mesh_verts_parallel_range_calc_normals_accum_cb, + &settings); + + /* Normalize the accumulated vertex normals. */ + BLI_task_parallel_range( + 0, verts_len, verts, mesh_verts_parallel_range_calc_normals_normalize_cb, &settings); + } + + if (edge_index_value != NULL) { + for (int i = 0; i < edges_len; i++) { + BMEdge *e = edges[i]; + BM_elem_index_set(e, edge_index_value[i]); /* set_ok (restore) */ + } + + MEM_freeN(edge_index_value); + } + + MEM_freeN(edgevec); +} + /** * \brief BMesh Compute Normals from/to external data. * @@ -1724,7 +1857,7 @@ static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do if (use_sel_face_history) { /* Using face history allows to select a single loop from a single face... - * Note that this is On² piece of code, + * Note that this is O(n^2) piece of code, * but it is not designed to be used with huge selection sets, * rather with only a few items selected at most.*/ /* Goes from last selected to the first selected element. */ diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index c1c2f17d7c1..708786a4c55 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -25,6 +25,7 @@ struct BMAllocTemplate; struct BMLoopNorEditDataArray; struct MLoopNorSpaceArray; +struct BMPartialUpdate; void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); @@ -41,6 +42,8 @@ void BM_mesh_data_free(BMesh *bm); void BM_mesh_clear(BMesh *bm); void BM_mesh_normals_update(BMesh *bm); +void BM_mesh_normals_update_with_partial(BMesh *bm, const struct BMPartialUpdate *bmpinfo); + void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c new file mode 100644 index 00000000000..2290e58fe6c --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c @@ -0,0 +1,274 @@ +/* + * 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. + */ + +/** \file + * \ingroup bmesh + * + * Generate data needed for partially updating mesh information. + * Currently this is used for normals and tessellation. + * + * Transform is the obvious use case where there is no need to update normals or tessellation + * for geometry which has not been modified. + * + * In the future this could be integrated into GPU updates too. + * + * Potential Improvements + * ====================== + * + * Some calculations could be significantly limited in the case of affine transformations + * (tessellation is an obvious candidate). Where only faces which have a mix + * of tagged and untagged vertices would need to be recalculated. + * + * In general this would work well besides some corner cases such as scaling to zero. + * Although the exact result may depend on the normal (for N-GONS), + * so for now update the tessellation of all tagged geometry. + */ + +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_bitmap.h" +#include "BLI_math_vector.h" + +#include "bmesh.h" + +/** + * Grow by 1.5x (rounding up). + * + * \note Use conservative reallocation since the initial sizes reserved + * may be close to (or exactly) the number of elements needed. + */ +#define GROW(len_alloc) ((len_alloc) + ((len_alloc) - ((len_alloc) / 2))) +#define GROW_ARRAY(mem, len_alloc) \ + { \ + mem = MEM_reallocN(mem, (sizeof(*mem)) * ((len_alloc) = GROW(len_alloc))); \ + } \ + ((void)0) + +#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index) \ + if (UNLIKELY(len_alloc == index)) { \ + GROW_ARRAY(mem, len_alloc); \ + } + +BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo, + BLI_bitmap *verts_tag, + BMVert *v) +{ + const int i = BM_elem_index_get(v); + if (!BLI_BITMAP_TEST(verts_tag, i)) { + BLI_BITMAP_ENABLE(verts_tag, i); + GROW_ARRAY_AS_NEEDED(bmpinfo->verts, bmpinfo->verts_len_alloc, bmpinfo->verts_len); + bmpinfo->verts[bmpinfo->verts_len++] = v; + return true; + } + return false; +} + +BLI_INLINE bool partial_elem_edge_ensure(BMPartialUpdate *bmpinfo, + BLI_bitmap *edges_tag, + BMEdge *e) +{ + const int i = BM_elem_index_get(e); + if (!BLI_BITMAP_TEST(edges_tag, i)) { + BLI_BITMAP_ENABLE(edges_tag, i); + GROW_ARRAY_AS_NEEDED(bmpinfo->edges, bmpinfo->edges_len_alloc, bmpinfo->edges_len); + bmpinfo->edges[bmpinfo->edges_len++] = e; + return true; + } + return false; +} + +BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo, + BLI_bitmap *faces_tag, + BMFace *f) +{ + const int i = BM_elem_index_get(f); + if (!BLI_BITMAP_TEST(faces_tag, i)) { + BLI_BITMAP_ENABLE(faces_tag, i); + GROW_ARRAY_AS_NEEDED(bmpinfo->faces, bmpinfo->faces_len_alloc, bmpinfo->faces_len); + bmpinfo->faces[bmpinfo->faces_len++] = f; + return true; + } + return false; +} + +BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, + const BMPartialUpdate_Params *params, + const int verts_len, + bool (*filter_fn)(BMVert *, void *user_data), + void *user_data) +{ + /* The caller is doing something wrong if this isn't the case. */ + BLI_assert(verts_len <= bm->totvert); + + BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__); + + /* Reserve more edges than vertices since it's common for a grid topology + * to use around twice as many edges as vertices. */ + const int default_verts_len_alloc = verts_len; + const int default_edges_len_alloc = min_ii(bm->totedge, verts_len * 2); + const int default_faces_len_alloc = min_ii(bm->totface, verts_len); + + /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags. + * Further, walking over all geometry to clear the tags isn't so efficient. */ + BLI_bitmap *verts_tag = NULL; + BLI_bitmap *edges_tag = NULL; + BLI_bitmap *faces_tag = NULL; + + /* Set vert inline. */ + BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE)); + + if (params->do_normals || params->do_tessellate) { + /* - Extend to all vertices connected faces: + * In the case of tessellation this is enough. + * + * In the case of vertex normal calculation, + * All the relevant connectivity data can be accessed from the faces + * (there is no advantage in storing connected edges or vertices in this pass). + * + * NOTE: In the future it may be useful to differentiate between vertices + * that are directly marked (by the filter function when looping over all vertices). + * And vertices marked from indirect connections. + * This would require an extra tag array, so avoid this unless it's needed. + */ + + /* Faces. */ + if (bmpinfo->faces == NULL) { + bmpinfo->faces_len_alloc = default_faces_len_alloc; + bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__); + faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__); + } + + BMVert *v; + BMIter iter; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_index_set(v, i); /* set_inline */ + if (!filter_fn(v, user_data)) { + continue; + } + BMEdge *e_iter = v->e; + if (e_iter != NULL) { + /* Loop over edges. */ + BMEdge *e_first = v->e; + do { + BMLoop *l_iter = e_iter->l; + if (e_iter->l != NULL) { + BMLoop *l_first = e_iter->l; + /* Loop over radial loops. */ + do { + if (l_iter->v == v) { + partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f); + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + } + } + } + + const int faces_len_direct = bmpinfo->faces_len; + + if (params->do_normals) { + /* - Extend to all faces vertices: + * Any changes to the faces normal needs to update all surrounding vertices. + * + * - Extend to all these vertices connected edges: + * These and needed to access those vertices edge vectors in normal calculation logic. + */ + + /* Vertices. */ + if (bmpinfo->verts == NULL) { + bmpinfo->verts_len_alloc = default_verts_len_alloc; + bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__); + verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__); + } + + /* Edges. */ + if (bmpinfo->edges == NULL) { + bmpinfo->edges_len_alloc = default_edges_len_alloc; + bmpinfo->edges = MEM_mallocN((sizeof(BMEdge *) * bmpinfo->edges_len_alloc), __func__); + edges_tag = BLI_BITMAP_NEW((size_t)bm->totedge, __func__); + } + + for (int i = 0; i < bmpinfo->faces_len; i++) { + BMFace *f = bmpinfo->faces[i]; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v)) { + continue; + } + BMVert *v = l_iter->v; + BMEdge *e_first = v->e; + BMEdge *e_iter = e_first; + do { + if (e_iter->l) { + if (!partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter)) { + continue; + } + + /* These faces need to be taken into account when weighting vertex normals + * but aren't needed for tessellation nor do their normals need to be recalculated. + * These faces end up between `faces_len` and `faces_len_normal_calc_accumulate` + * in the faces array. */ + BMLoop *l_first_radial = e_iter->l; + BMLoop *l_iter_radial = l_first_radial; + /* Loop over radial loops. */ + do { + if (l_iter_radial->v == v) { + partial_elem_face_ensure(bmpinfo, faces_tag, l_iter_radial->f); + } + } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + } while ((l_iter = l_iter->next) != l_first); + } + } + + bmpinfo->faces_len_normal_calc_accumulate = bmpinfo->faces_len; + bmpinfo->faces_len = faces_len_direct; + + if (verts_tag) { + MEM_freeN(verts_tag); + } + if (edges_tag) { + MEM_freeN(edges_tag); + } + if (faces_tag) { + MEM_freeN(faces_tag); + } + + bmpinfo->params = *params; + + return bmpinfo; +} + +void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) +{ + if (bmpinfo->verts) { + MEM_freeN(bmpinfo->verts); + } + if (bmpinfo->edges) { + MEM_freeN(bmpinfo->edges); + } + if (bmpinfo->faces) { + MEM_freeN(bmpinfo->faces); + } + MEM_freeN(bmpinfo); +} diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h new file mode 100644 index 00000000000..c0c9b275fa4 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bmesh + */ + +#include "BLI_compiler_attrs.h" + +/** + * Parameters used to determine which kinds of data needs to be generated. + */ +typedef struct BMPartialUpdate_Params { + bool do_normals; + bool do_tessellate; +} BMPartialUpdate_Params; + +/** + * Cached data to speed up partial updates. + * + * Hints: + * + * - Avoid creating this data for single updates, + * it should be created and reused across multiple updates to gain a significant benefit + * (while transforming geometry for example). + * + * - Partial normal updates use face & loop indices, + * setting them to dirty values between updates will slow down normal recalculation. + */ +typedef struct BMPartialUpdate { + BMVert **verts; + BMEdge **edges; + BMFace **faces; + int verts_len, verts_len_alloc; + int edges_len, edges_len_alloc; + int faces_len, faces_len_alloc; + /** + * Faces at the end of `faces` that don't need to have the normals recalculated + * but must be included when waiting the vertex normals. + */ + int faces_len_normal_calc_accumulate; + + /** Store the parameters used in creation so invalid use can be asserted. */ + BMPartialUpdate_Params params; +} BMPartialUpdate; + +BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, + const BMPartialUpdate_Params *params, + const int verts_len, + bool (*filter_fn)(BMVert *, void *user_data), + void *user_data) + ATTR_NONNULL(1, 2, 4) ATTR_WARN_UNUSED_RESULT; + +void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) ATTR_NONNULL(1); diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c new file mode 100644 index 00000000000..f2a5fbe3bde --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -0,0 +1,405 @@ +/* + * 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. + */ + +/** \file + * \ingroup bmesh + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + */ + +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_heap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_polyfill_2d_beautify.h" +#include "BLI_task.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +/* -------------------------------------------------------------------- */ +/** \name Default Mesh Tessellation + * \{ */ + +static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p) +{ + switch (efa->len) { + case 3: { + /* `0 1 2` -> `0 1 2` */ + BMLoop *l; + BMLoop **l_ptr = looptris[0]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + return 1; + } + case 4: { + /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ + BMLoop *l; + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; + (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); + (l_ptr_a[1] = l = l->next); + (l_ptr_a[2] = l_ptr_b[1] = l = l->next); + (l_ptr_b[2] = l->next); + + if (UNLIKELY(is_quad_flip_v3_first_third_fast( + l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { + /* Flip out of degenerate 0-2 state. */ + l_ptr_a[2] = l_ptr_b[2]; + l_ptr_b[0] = l_ptr_a[1]; + } + return 2; + } + default: { + BMLoop *l_iter, *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const int tris_len = efa->len - 2; + + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); + + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(pf_arena); + return tris_len; + } + } +} + +/** + * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh + * \param looptris: + * + * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count + */ +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena); + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = NULL; + } + + BLI_assert(i <= looptris_tot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Default Tessellation (Partial Updates) + * \{ */ + +struct PartialTessellationUserData { + BMFace **faces; + BMLoop *(*looptris)[3]; +}; + +struct PartialTessellationUserTLS { + MemArena *pf_arena; +}; + +static void mesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk; + struct PartialTessellationUserData *data = userdata; + BMFace *f = data->faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + mesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena); +} + +static void mesh_calc_tessellation_for_face_partial_free_fn( + const void *__restrict UNUSED(userdata), void *__restrict tls_v) +{ + struct PartialTessellationUserTLS *tls_data = tls_v; + if (tls_data->pf_arena) { + BLI_memarena_free(tls_data->pf_arena); + } +} + +static void bm_mesh_calc_tessellation_with_partial__multi_threaded(BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + const int faces_len = bmpinfo->faces_len; + BMFace **faces = bmpinfo->faces; + + struct PartialTessellationUserData data = { + .faces = faces, + .looptris = looptris, + }; + struct PartialTessellationUserTLS tls_dummy = {NULL}; + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = true; + settings.userdata_chunk = &tls_dummy; + settings.userdata_chunk_size = sizeof(tls_dummy); + settings.func_free = mesh_calc_tessellation_for_face_partial_free_fn; + + BLI_task_parallel_range( + 0, faces_len, &data, mesh_calc_tessellation_for_face_partial_fn, &settings); +} + +static void bm_mesh_calc_tessellation_with_partial__single_threaded(BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + const int faces_len = bmpinfo->faces_len; + BMFace **faces = bmpinfo->faces; + + MemArena *pf_arena = NULL; + + for (int index = 0; index < faces_len; index++) { + BMFace *f = faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + mesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena); + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + } +} + +void BM_mesh_calc_tessellation_with_partial(BMesh *bm, + BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + BLI_assert(bmpinfo->params.do_tessellate); + + BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE); + + /* On systems with 32+ cores, + * only a very small number of faces has any advantage single threading (in the 100's). + * Note that between 500-2000 quads, the difference isn't so much + * (tessellation isn't a bottleneck in this case anyway). + * Avoid the slight overhead of using threads in this case. */ + if (bmpinfo->faces_len < 1024) { + bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo); + } + else { + bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Beauty Mesh Tessellation + * + * Avoid degenerate triangles. + * \{ */ + +static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p, + Heap **pf_heap_p) +{ + switch (efa->len) { + case 3: { + BMLoop *l; + BMLoop **l_ptr = looptris[0]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + return 1; + } + case 4: { + BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); + BMLoop *l_v2 = l_v1->next; + BMLoop *l_v3 = l_v2->next; + BMLoop *l_v4 = l_v1->prev; + + /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! + * It's meant for rotating edges, it also calculates a new normal. + * + * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. + */ +#if 0 + const bool split_13 = (BM_verts_calc_rotate_beauty( + l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); +#else + float axis_mat[3][3], v_quad[4][2]; + axis_dominant_v3_to_m3(axis_mat, efa->no); + mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); + mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); + mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); + mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); + + const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( + v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; +#endif + + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; + if (split_13) { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v3; + + l_ptr_b[0] = l_v1; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + else { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v4; + + l_ptr_b[0] = l_v2; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + return 2; + } + default: { + MemArena *pf_arena = *pf_arena_p; + Heap *pf_heap = *pf_heap_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + } + + BMLoop *l_iter, *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const int tris_len = efa->len - 2; + + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); + + BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); + + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(pf_arena); + + return tris_len; + } + } +} + +/** + * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + */ +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + /* use_beauty */ + Heap *pf_heap = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap); + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + + BLI_heap_free(pf_heap, NULL); + } + + BLI_assert(i <= looptris_tot); +} + +/** \} */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h new file mode 100644 index 00000000000..f68a91cb988 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bmesh + */ + +struct BMPartialUpdate; + +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); + +void BM_mesh_calc_tessellation_with_partial(BMesh *bm, + BMLoop *(*looptris)[3], + const struct BMPartialUpdate *bmpinfo); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 4ae2cc67140..5397098a7f3 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -18,8 +18,7 @@ * \ingroup bmesh * * This file contains code for dealing - * with polygons (normal/area calculation, - * tessellation, etc) + * with polygons (normal/area calculation, tessellation, etc) */ #include "DNA_listBase.h" @@ -1523,289 +1522,3 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) l = l->next; r_loops[3] = l; } - -/** - * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh - * \param looptris: - * - * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count - */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) -{ - /* use this to avoid locking pthread for _every_ polygon - * and calling the fill function */ -#define USE_TESSFACE_SPEEDUP - - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *arena = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ - } - -#ifdef USE_TESSFACE_SPEEDUP - - /* no need to ensure the loop order, we know its ok */ - - else if (efa->len == 3) { -# if 0 - int j; - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - looptris[i][j] = l; - } - i += 1; -# else - /* more cryptic but faster */ - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; -# endif - } - else if (efa->len == 4) { -# if 0 - BMLoop *ltmp[4]; - int j; - BLI_array_grow_items(looptris, 2); - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - ltmp[j] = l; - } - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[1]; - looptris[i][2] = ltmp[2]; - i += 1; - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[2]; - looptris[i][2] = ltmp[3]; - i += 1; -# else - /* more cryptic but faster */ - BMLoop *l; - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); - (l_ptr_a[1] = l = l->next); - (l_ptr_a[2] = l_ptr_b[1] = l = l->next); - (l_ptr_b[2] = l->next); -# endif - - if (UNLIKELY(is_quad_flip_v3_first_third_fast( - l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { - /* flip out of degenerate 0-2 state. */ - l_ptr_a[2] = l_ptr_b[2]; - l_ptr_b[0] = l_ptr_a[1]; - } - } - -#endif /* USE_TESSFACE_SPEEDUP */ - - else { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - uint(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - uint *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - *r_looptris_tot = i; - - BLI_assert(i <= looptris_tot); - -#undef USE_TESSFACE_SPEEDUP -} - -/** - * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. - */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) -{ - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *pf_arena = NULL; - - /* use_beauty */ - Heap *pf_heap = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ - } - else if (efa->len == 3) { - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; - } - else if (efa->len == 4) { - BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); - BMLoop *l_v2 = l_v1->next; - BMLoop *l_v3 = l_v2->next; - BMLoop *l_v4 = l_v1->prev; - - /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! - * It's meant for rotating edges, it also calculates a new normal. - * - * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. - */ -#if 0 - const bool split_13 = (BM_verts_calc_rotate_beauty( - l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); -#else - float axis_mat[3][3], v_quad[4][2]; - axis_dominant_v3_to_m3(axis_mat, efa->no); - mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); - mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); - mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); - mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); - - const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( - v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; -#endif - - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - if (split_13) { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v3; - - l_ptr_b[0] = l_v1; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - else { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v4; - - l_ptr_b[0] = l_v2; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - } - else { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); - } - - tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); - - BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - unsigned int *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(pf_arena); - } - } - - if (pf_arena) { - BLI_memarena_free(pf_arena); - - BLI_heap_free(pf_heap, NULL); - } - - *r_looptris_tot = i; - - BLI_assert(i <= looptris_tot); -} diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 8c2b9ee0bff..2c32cd39002 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -21,12 +21,10 @@ */ struct Heap; +struct BMPartialUpdate; #include "BLI_compiler_attrs.h" -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); - void BM_face_calc_tessellation(const BMFace *f, const bool use_fixed_quad, BMLoop **r_loops, diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 65391794c12..ac59d832013 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -49,6 +49,8 @@ set(SRC COM_compositor.h COM_defines.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h intern/COM_CPUDevice.cc intern/COM_CPUDevice.h intern/COM_ChunkOrder.cc @@ -66,14 +68,20 @@ set(SRC intern/COM_Enums.cc intern/COM_ExecutionGroup.cc intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h intern/COM_ExecutionSystem.cc intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h intern/COM_MemoryBuffer.cc intern/COM_MemoryBuffer.h intern/COM_MemoryProxy.cc intern/COM_MemoryProxy.h intern/COM_MetaData.cc intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h intern/COM_Node.cc intern/COM_Node.h intern/COM_NodeConverter.cc @@ -86,8 +94,12 @@ set(SRC intern/COM_NodeOperationBuilder.h intern/COM_OpenCLDevice.cc intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h intern/COM_SingleThreadedOperation.cc intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h intern/COM_WorkPackage.cc intern/COM_WorkPackage.h intern/COM_WorkScheduler.cc diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index ef889807030..5a52d216117 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -20,6 +20,16 @@ namespace blender::compositor { +enum class eExecutionModel { + /** + * Operations are executed from outputs to inputs grouped in execution groups and rendered + * in tiles. + */ + Tiled, + /** Operations are fully rendered in order from inputs to outputs. */ + FullFrame +}; + /** * \brief possible data types for sockets * \ingroup Model diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc new file mode 100644 index 00000000000..8464d01801f --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_BufferOperation.h" + +namespace blender::compositor { + +BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) +{ + buffer_ = buffer; + /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following + * code to: set_resolution(buffer.get_size()) */ + unsigned int resolution[2]; + resolution[0] = buffer->getWidth(); + resolution[1] = buffer->getHeight(); + setResolution(resolution); + addOutputSocket(data_type); +} + +void *BufferOperation::initializeTileData(rcti * /*rect*/) +{ + return buffer_; +} + +void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +{ + switch (sampler) { + case PixelSampler::Nearest: + buffer_->read(output, x, y); + break; + case PixelSampler::Bilinear: + default: + buffer_->readBilinear(output, x, y); + break; + case PixelSampler::Bicubic: + /* No bicubic. Same implementation as ReadBufferOperation. */ + buffer_->readBilinear(output, x, y); + break; + } +} + +void BufferOperation::executePixelFiltered( + float output[4], float x, float y, float dx[2], float dy[2]) +{ + const float uv[2] = {x, y}; + const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; + buffer_->readEWA(output, uv, deriv); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h new file mode 100644 index 00000000000..f87cd4db94e --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +class BufferOperation : public NodeOperation { + private: + MemoryBuffer *buffer_; + + public: + BufferOperation(MemoryBuffer *buffer, DataType data_type); + + void *initializeTileData(rcti *rect) override; + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc index 29a82bec636..2ca5557e278 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.cc +++ b/source/blender/compositor/intern/COM_CPUDevice.cc @@ -30,11 +30,24 @@ CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id) void CPUDevice::execute(WorkPackage *work_package) { - const unsigned int chunkNumber = work_package->chunk_number; - ExecutionGroup *executionGroup = work_package->execution_group; - - executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); - executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + switch (work_package->type) { + case eWorkPackageType::Tile: { + const unsigned int chunkNumber = work_package->chunk_number; + ExecutionGroup *executionGroup = work_package->execution_group; + + executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); + executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + break; + } + case eWorkPackageType::CustomFunction: { + work_package->execute_fn(); + break; + } + } + + if (work_package->executed_fn) { + work_package->executed_fn(); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index f70f3a8ebfc..61e299c045e 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -21,6 +21,7 @@ #include <cstdio> #include "BLI_assert.h" +#include "DNA_userdef_types.h" namespace blender::compositor { @@ -33,6 +34,7 @@ CompositorContext::CompositorContext() this->m_fastCalculation = false; this->m_viewSettings = nullptr; this->m_displaySettings = nullptr; + this->m_bnodetree = nullptr; } int CompositorContext::getFramenumber() const @@ -41,4 +43,20 @@ int CompositorContext::getFramenumber() const return m_rd->cfra; } +eExecutionModel CompositorContext::get_execution_model() const +{ + if (U.experimental.use_full_frame_compositor) { + BLI_assert(m_bnodetree != nullptr); + switch (m_bnodetree->execution_mode) { + case 1: + return eExecutionModel::FullFrame; + case 0: + return eExecutionModel::Tiled; + default: + BLI_assert(!"Invalid execution mode"); + } + } + return eExecutionModel::Tiled; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index e6164246bdd..56251511576 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -281,6 +281,11 @@ class CompositorContext { { return m_rd->size * 0.01f; } + + /** + * Get active execution model. + */ + eExecutionModel get_execution_model() const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index dfb4f53fee5..4cf7e09a7d8 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -211,12 +211,14 @@ int DebugInfo::graphviz_legend_group( return len; } -int DebugInfo::graphviz_legend(char *str, int maxlen) +int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups) { int len = 0; len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n"); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + } len += snprintf( str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n"); @@ -236,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen) "Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0); len += graphviz_legend_color( "Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += graphviz_legend_color( + "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_color( + "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + } len += graphviz_legend_color( "Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n"); - - len += graphviz_legend_group( - "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n"); + len += graphviz_legend_group( + "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n"); @@ -387,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } } - len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0); + const bool has_execution_groups = system->getContext().get_execution_model() == + eExecutionModel::Tiled; + len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n"); diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index e1aea69e481..0de3a5e39dc 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -129,7 +129,7 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend_group( const char *name, const char *color, const char *style, char *str, int maxlen); - static int graphviz_legend(char *str, int maxlen); + static int graphviz_legend(char *str, int maxlen, bool has_execution_groups); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); }; diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h index f65ce3e856e..519e7df940e 100644 --- a/source/blender/compositor/intern/COM_Enums.h +++ b/source/blender/compositor/intern/COM_Enums.h @@ -70,6 +70,21 @@ enum class eWorkPackageState { Executed = 2, }; +/** + * \brief Work type to execute. + * \ingroup Execution + */ +enum class eWorkPackageType { + /** + * \brief Executes an execution group tile. + */ + Tile = 0, + /** + * \brief Executes a custom function. + */ + CustomFunction = 1 +}; + std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority); std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state); diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 80d453bf7f9..68bda8c70d6 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -157,6 +157,7 @@ void ExecutionGroup::init_work_packages() if (this->m_chunks_len != 0) { m_work_packages.resize(this->m_chunks_len); for (unsigned int index = 0; index < m_chunks_len; index++) { + m_work_packages[index].type = eWorkPackageType::Tile; m_work_packages[index].state = eWorkPackageState::NotScheduled; m_work_packages[index].execution_group = this; m_work_packages[index].chunk_number = index; diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc new file mode 100644 index 00000000000..4d7f62e091b --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_ExecutionModel.h" + +namespace blender::compositor { + +ExecutionModel::ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations) + : context_(context), operations_(operations) +{ + const bNodeTree *node_tree = context_.getbNodeTree(); + + const rctf *viewer_border = &node_tree->viewer_border; + border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) && + viewer_border->xmin < viewer_border->xmax && + viewer_border->ymin < viewer_border->ymax; + border_.viewer_border = viewer_border; + + const RenderData *rd = context_.getRenderData(); + /* Case when cropping to render border happens is handled in + * compositor output and render layer nodes. */ + border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) && + !(rd->mode & R_CROP); + border_.render_border = &rd->border; +} + +bool ExecutionModel::is_breaked() const +{ + const bNodeTree *btree = context_.getbNodeTree(); + return btree->test_break(btree->tbh); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h new file mode 100644 index 00000000000..9e8466b9282 --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.h @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_rect.h" +#include "BLI_vector.hh" + +#include "COM_ExecutionSystem.h" + +#include <functional> + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class NodeOperation; + +/** + * Base class for execution models. Contains shared implementation. + */ +class ExecutionModel { + protected: + /** + * Render and viewer border info. Coordinates are normalized. + */ + struct { + bool use_render_border; + const rctf *render_border; + bool use_viewer_border; + const rctf *viewer_border; + } border_; + + /** + * Context used during execution. + */ + CompositorContext &context_; + + /** + * All operations being executed. + */ + Span<NodeOperation *> operations_; + + public: + ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations); + + virtual ~ExecutionModel() + { + } + + virtual void execute(ExecutionSystem &exec_system) = 0; + + virtual void execute_work(const rcti &UNUSED(work_rect), + std::function<void(const rcti &split_rect)> UNUSED(work_func)) + { + BLI_assert(!"Method not supported by current execution model"); + } + + protected: + bool is_breaked() const; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index e22dc17837b..a12ec774032 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -21,16 +21,11 @@ #include "BLI_utildefines.h" #include "PIL_time.h" -#include "BKE_node.h" - -#include "BLT_translation.h" - -#include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_ExecutionGroup.h" +#include "COM_FullFrameExecutionModel.h" #include "COM_NodeOperation.h" #include "COM_NodeOperationBuilder.h" -#include "COM_ReadBufferOperation.h" +#include "COM_TiledExecutionModel.h" #include "COM_WorkScheduler.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -73,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, builder.convertToOperations(this); } - unsigned int resolution[2]; - - rctf *viewer_border = &editingtree->viewer_border; - bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) && - viewer_border->xmin < viewer_border->xmax && - viewer_border->ymin < viewer_border->ymax; - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution")); - - for (ExecutionGroup *executionGroup : m_groups) { - resolution[0] = 0; - resolution[1] = 0; - executionGroup->determineResolution(resolution); - - if (rendering) { - /* case when cropping to render border happens is handled in - * compositor output and render layer nodes - */ - if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) { - executionGroup->setRenderBorder( - rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax); - } - } - - if (use_viewer_border) { - executionGroup->setViewerBorder( - viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); - } + switch (m_context.get_execution_model()) { + case eExecutionModel::Tiled: + execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups); + break; + case eExecutionModel::FullFrame: + execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); + break; + default: + BLI_assert(!"Non implemented execution model"); + break; } - - // DebugInfo::graphviz(this); } ExecutionSystem::~ExecutionSystem() { + delete execution_model_; + for (NodeOperation *operation : m_operations) { delete operation; } @@ -126,100 +103,16 @@ void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations, m_groups = groups; } -static void update_read_buffer_offset(Vector<NodeOperation *> &operations) -{ - unsigned int order = 0; - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; - readOperation->setOffset(order); - order++; - } - } -} - -static void init_write_operations_for_execution(Vector<NodeOperation *> &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void link_write_buffers(Vector<NodeOperation *> &operations) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation); - readOperation->updateMemoryBuffer(); - } - } -} - -static void init_non_write_operations_for_execution(Vector<NodeOperation *> &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (!operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void init_execution_groups_for_execution(Vector<ExecutionGroup *> &groups, - const int chunk_size) -{ - for (ExecutionGroup *execution_group : groups) { - execution_group->setChunksize(chunk_size); - execution_group->initExecution(); - } -} - void ExecutionSystem::execute() { - const bNodeTree *editingtree = this->m_context.getbNodeTree(); - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); - DebugInfo::execute_started(this); - update_read_buffer_offset(m_operations); - - init_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - link_write_buffers(m_operations); - init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - init_execution_groups_for_execution(m_groups, m_context.getChunksize()); - - WorkScheduler::start(this->m_context); - execute_groups(eCompositorPriority::High); - if (!this->getContext().isFastCalculation()) { - execute_groups(eCompositorPriority::Medium); - execute_groups(eCompositorPriority::Low); - } - WorkScheduler::finish(); - WorkScheduler::stop(); - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); - - for (NodeOperation *operation : m_operations) { - operation->deinitExecution(); - } - - for (ExecutionGroup *execution_group : m_groups) { - execution_group->deinitExecution(); - } + execution_model_->execute(*this); } -void ExecutionSystem::execute_groups(eCompositorPriority priority) +void ExecutionSystem::execute_work(const rcti &work_rect, + std::function<void(const rcti &split_rect)> work_func) { - for (ExecutionGroup *execution_group : m_groups) { - if (execution_group->get_flags().is_output && - execution_group->getRenderPriority() == priority) { - execution_group->execute(this); - } - } + execution_model_->execute_work(work_rect, work_func); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index e6170c48778..e106209651c 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -25,6 +25,7 @@ class ExecutionGroup; #include "COM_ExecutionGroup.h" #include "COM_Node.h" #include "COM_NodeOperation.h" +#include "COM_SharedOperationBuffers.h" #include "DNA_color_types.h" #include "DNA_node_types.h" @@ -115,13 +116,21 @@ namespace blender::compositor { * \see ExecutionGroup class representing the ExecutionGroup */ +/* Forward declarations. */ +class ExecutionModel; + /** * \brief the ExecutionSystem contains the whole compositor tree. */ class ExecutionSystem { - private: /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers active_buffers_; + + /** * \brief the context used during execution */ CompositorContext m_context; @@ -136,6 +145,11 @@ class ExecutionSystem { */ Vector<ExecutionGroup *> m_groups; + /** + * Active execution model implementation. + */ + ExecutionModel *execution_model_; + private: // methods public: /** @@ -178,9 +192,14 @@ class ExecutionSystem { return this->m_context; } - private: - void execute_groups(eCompositorPriority priority); + SharedOperationBuffers &get_active_buffers() + { + return active_buffers_; + } + + void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func); + private: /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc new file mode 100644 index 00000000000..396aa2fcf6f --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -0,0 +1,327 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_FullFrameExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span<NodeOperation *> operations) + : ExecutionModel(context, operations), + active_buffers_(shared_buffers), + num_operations_finished_(0), + work_mutex_(), + work_finished_cond_() +{ + priorities_.append(eCompositorPriority::High); + if (!context.isFastCalculation()) { + priorities_.append(eCompositorPriority::Medium); + priorities_.append(eCompositorPriority::Low); + } + + BLI_mutex_init(&work_mutex_); + BLI_condition_init(&work_finished_cond_); +} + +FullFrameExecutionModel::~FullFrameExecutionModel() +{ + BLI_condition_end(&work_finished_cond_); + BLI_mutex_end(&work_mutex_); +} + +void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *node_tree = this->context_.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); + + DebugInfo::graphviz(&exec_system); + + determine_areas_to_render_and_reads(); + render_operations(exec_system); +} + +void FullFrameExecutionModel::determine_areas_to_render_and_reads() +{ + const bool is_rendering = context_.isRendering(); + const bNodeTree *node_tree = context_.getbNodeTree(); + + rcti area; + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + op->setbNodeTree(node_tree); + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + get_output_render_area(op, area); + determine_areas_to_render(op, area); + determine_reads(op); + } + } + } +} + +void FullFrameExecutionModel::ensure_inputs_rendered(NodeOperation *op, + ExecutionSystem &exec_system) +{ + const int num_inputs = op->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + if (!active_buffers_.is_operation_rendered(input_op)) { + render_operation(input_op, exec_system); + } + } +} + +Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op) +{ + const int num_inputs = op->getNumberOfInputSockets(); + Vector<MemoryBuffer *> inputs_buffers(num_inputs); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op); + } + return inputs_buffers; +} + +MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) +{ + rcti op_rect; + BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); + + const DataType data_type = op->getOutputSocket(0)->getDataType(); + /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way + * to know if an operation is constant has to be implemented yet. */ + const bool is_a_single_elem = op->get_flags().is_set_operation; + return new MemoryBuffer(data_type, op_rect, is_a_single_elem); +} + +void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system) +{ + if (active_buffers_.is_operation_rendered(op)) { + return; + } + + ensure_inputs_rendered(op, exec_system); + Vector<MemoryBuffer *> input_bufs = get_input_buffers(op); + + const bool has_outputs = op->getNumberOfOutputSockets() > 0; + MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; + Span<rcti> areas = active_buffers_.get_areas_to_render(op); + op->render(op_buf, areas, input_bufs, exec_system); + active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf)); + + operation_finished(op); +} + +/** + * Render output operations in order of priority. + */ +void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system) +{ + const bool is_rendering = context_.isRendering(); + + WorkScheduler::start(this->context_); + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + render_operation(op, exec_system); + } + } + } + WorkScheduler::stop(); +} + +/** + * Determines all input operations areas needed to render given operation area. + * \param operation: Renderer operation. + * \param render_area: Area within given operation bounds to render. + */ +void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *operation, + const rcti &render_area) +{ + if (active_buffers_.is_area_registered(operation, render_area)) { + return; + } + + active_buffers_.register_area(operation, render_area); + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + rcti input_op_rect, input_area; + BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight()); + operation->get_area_of_interest(input_op, render_area, input_area); + + /* Ensure area of interest is within operation bounds, cropping areas outside. */ + BLI_rcti_isect(&input_area, &input_op_rect, &input_area); + + determine_areas_to_render(input_op, input_area); + } +} + +/** + * Determines the reads given operation and its inputs will receive (i.e: Number of dependent + * operations each operation has). + */ +void FullFrameExecutionModel::determine_reads(NodeOperation *operation) +{ + if (active_buffers_.has_registered_reads(operation)) { + return; + } + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + determine_reads(input_op); + active_buffers_.register_read(input_op); + } +} + +/** + * Calculates given output operation area to be rendered taking into account viewer and render + * borders. + */ +void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area) +{ + BLI_assert(output_op->isOutputOperation(context_.isRendering())); + + /* By default return operation bounds (no border). */ + const int op_width = output_op->getWidth(); + const int op_height = output_op->getHeight(); + BLI_rcti_init(&r_area, 0, op_width, 0, op_height); + + const bool has_viewer_border = border_.use_viewer_border && + (output_op->get_flags().is_viewer_operation || + output_op->get_flags().is_preview_operation); + const bool has_render_border = border_.use_render_border; + if (has_viewer_border || has_render_border) { + /* Get border with normalized coordinates. */ + const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; + + /* Return de-normalized border. */ + BLI_rcti_init(&r_area, + norm_border->xmin * op_width, + norm_border->xmax * op_width, + norm_border->ymin * op_height, + norm_border->ymax * op_height); + } +} + +/** + * Multi-threadedly execute given work function passing work_rect splits as argument. + */ +void FullFrameExecutionModel::execute_work(const rcti &work_rect, + std::function<void(const rcti &split_rect)> work_func) +{ + if (is_breaked()) { + return; + } + + /* Split work vertically to maximize continuous memory. */ + const int work_height = BLI_rcti_size_y(&work_rect); + const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height); + const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works; + int remaining_height = work_height - split_height * num_sub_works; + + Vector<WorkPackage> sub_works(num_sub_works); + int sub_work_y = work_rect.ymin; + int num_sub_works_finished = 0; + for (int i = 0; i < num_sub_works; i++) { + int sub_work_height = split_height; + + /* Distribute remaining height between sub-works. */ + if (remaining_height > 0) { + sub_work_height++; + remaining_height--; + } + + WorkPackage &sub_work = sub_works[i]; + sub_work.type = eWorkPackageType::CustomFunction; + sub_work.execute_fn = [=, &work_func, &work_rect]() { + if (is_breaked()) { + return; + } + rcti split_rect; + BLI_rcti_init( + &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height); + work_func(split_rect); + }; + sub_work.executed_fn = [&]() { + BLI_mutex_lock(&work_mutex_); + num_sub_works_finished++; + if (num_sub_works_finished == num_sub_works) { + BLI_condition_notify_one(&work_finished_cond_); + } + BLI_mutex_unlock(&work_mutex_); + }; + WorkScheduler::schedule(&sub_work); + sub_work_y += sub_work_height; + } + BLI_assert(sub_work_y == work_rect.ymax); + + WorkScheduler::finish(); + + /* Ensure all sub-works finished. + * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading + * model. Sync code should be removed once it's fixed. */ + BLI_mutex_lock(&work_mutex_); + if (num_sub_works_finished < num_sub_works) { + BLI_condition_wait(&work_finished_cond_, &work_mutex_); + } + BLI_mutex_unlock(&work_mutex_); +} + +void FullFrameExecutionModel::operation_finished(NodeOperation *operation) +{ + /* Report inputs reads so that buffers may be freed/reused. */ + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + active_buffers_.read_finished(operation->get_input_operation(i)); + } + + num_operations_finished_++; + update_progress_bar(); +} + +void FullFrameExecutionModel::update_progress_bar() +{ + const bNodeTree *tree = context_.getbNodeTree(); + if (tree) { + const float progress = num_operations_finished_ / static_cast<float>(operations_.size()); + tree->progress(tree->prh, progress); + + char buf[128]; + BLI_snprintf(buf, + sizeof(buf), + TIP_("Compositing | Operation %i-%li"), + num_operations_finished_ + 1, + operations_.size()); + tree->stats_draw(tree->sdh, buf); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h new file mode 100644 index 00000000000..2c0d5e0460a --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -0,0 +1,89 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +/* Forward declarations. */ +class ExecutionGroup; + +/** + * Fully renders operations in order from inputs to outputs. + */ +class FullFrameExecutionModel : public ExecutionModel { + private: + /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers &active_buffers_; + + /** + * Number of operations finished. + */ + int num_operations_finished_; + + /** + * Order of priorities for output operations execution. + */ + Vector<eCompositorPriority> priorities_; + + ThreadMutex work_mutex_; + ThreadCondition work_finished_cond_; + + public: + FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span<NodeOperation *> operations); + ~FullFrameExecutionModel(); + + void execute(ExecutionSystem &exec_system) override; + + void execute_work(const rcti &work_rect, + std::function<void(const rcti &split_rect)> work_func) override; + + private: + void determine_areas_to_render_and_reads(); + void render_operations(ExecutionSystem &exec_system); + + void ensure_inputs_rendered(NodeOperation *op, ExecutionSystem &exec_system); + Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op); + MemoryBuffer *create_operation_buffer(NodeOperation *op); + void render_operation(NodeOperation *op, ExecutionSystem &exec_system); + + void operation_finished(NodeOperation *operation); + + void get_output_render_area(NodeOperation *output_op, rcti &r_area); + void determine_areas_to_render(NodeOperation *operation, const rcti &render_area); + void determine_reads(NodeOperation *operation); + + void update_progress_bar(); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc new file mode 100644 index 00000000000..c54c2edccb0 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -0,0 +1,26 @@ +#include "COM_MultiThreadedOperation.h" +#include "COM_ExecutionSystem.h" + +namespace blender::compositor { + +MultiThreadedOperation::MultiThreadedOperation() +{ + m_num_passes = 1; + flags.is_fullframe_operation = true; +} + +void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &output_area, + blender::Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system) +{ + for (int current_pass = 0; current_pass < m_num_passes; current_pass++) { + update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass); + exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) { + update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass); + }); + update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h new file mode 100644 index 00000000000..97c5fba4ead --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +class MultiThreadedOperation : public NodeOperation { + protected: + /** + * Number of execution passes. + */ + int m_num_passes; + + protected: + MultiThreadedOperation(); + + /** + * Called before an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + /** + * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls. + */ + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &output_rect, + blender::Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system, + int current_pass) = 0; + + /** + * Called after an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + private: + void update_memory_buffer(MemoryBuffer *output, + const rcti &output_area, + blender::Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index be3ea59efa5..83de8a751c4 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -17,8 +17,10 @@ */ #include <cstdio> +#include <memory> #include <typeinfo> +#include "COM_BufferOperation.h" #include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" #include "COM_defines.h" @@ -175,6 +177,177 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input, return !first; } +/* -------------------------------------------------------------------- */ +/** \name Full Frame Methods + * \{ */ + +/** + * \brief Get input operation area being read by this operation on rendering given output area. + * + * Implementation don't need to ensure r_input_area is within input operation bounds. The + * caller must clamp it. + * TODO: See if it's possible to use parameter overloading (input_id for example). + * + * \param input_op_idx: Input operation index for which we want to calculate the area being read. + * \param output_area: Area being rendered by this operation. + * \param r_input_area: Returned input operation area that needs to be read in order to render + * given output area. + */ +void NodeOperation::get_area_of_interest(const int input_op_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (get_flags().is_fullframe_operation) { + r_input_area = output_area; + } + else { + /* Non full-frame operations never implement this method. To ensure correctness assume + * whole area is used. */ + NodeOperation *input_op = getInputOperation(input_op_idx); + BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); + } +} + +void NodeOperation::get_area_of_interest(NodeOperation *input_op, + const rcti &output_area, + rcti &r_input_area) +{ + for (int i = 0; i < getNumberOfInputSockets(); i++) { + if (input_op == getInputOperation(i)) { + get_area_of_interest(i, output_area, r_input_area); + return; + } + } + BLI_assert(!"input_op is not an input operation."); +} + +/** + * Executes operation image manipulation algorithm rendering given areas. + * \param output_buf: Buffer to write result to. + * \param areas: Areas within this operation bounds to render. + * \param inputs_bufs: Inputs operations buffers. + * \param exec_system: Execution system. + */ +void NodeOperation::render(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system) +{ + if (get_flags().is_fullframe_operation) { + render_full_frame(output_buf, areas, inputs_bufs, exec_system); + } + else { + render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system); + } +} + +/** + * Renders given areas using operations full frame implementation. + */ +void NodeOperation::render_full_frame(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system) +{ + initExecution(); + for (const rcti &area : areas) { + update_memory_buffer(output_buf, area, inputs_bufs, exec_system); + } + deinitExecution(); +} + +/** + * Renders given areas using operations tiled implementation. + */ +void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system) +{ + Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs); + + initExecution(); + const bool is_output_operation = getNumberOfOutputSockets() == 0; + if (!is_output_operation && output_buf->is_a_single_elem()) { + float *output_elem = output_buf->get_elem(0, 0); + readSampled(output_elem, 0, 0, PixelSampler::Nearest); + } + else { + for (const rcti &rect : areas) { + exec_system.execute_work(rect, [=](const rcti &split_rect) { + rcti tile_rect = split_rect; + if (is_output_operation) { + executeRegion(&tile_rect, 0); + } + else { + render_tile(output_buf, &tile_rect); + } + }); + } + } + deinitExecution(); + + remove_buffers_and_restore_original_inputs(orig_input_links); +} + +void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) +{ + const bool is_complex = get_flags().complex; + void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr; + const int elem_stride = output_buf->elem_stride; + for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) { + float *output_elem = output_buf->get_elem(tile_rect->xmin, y); + if (is_complex) { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + read(output_elem, x, y, tile_data); + output_elem += elem_stride; + } + } + else { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + readSampled(output_elem, x, y, PixelSampler::Nearest); + output_elem += elem_stride; + } + } + } + if (tile_data) { + deinitializeTileData(tile_rect, tile_data); + } +} + +/** + * \return Replaced inputs links. + */ +Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers( + Span<MemoryBuffer *> inputs_bufs) +{ + BLI_assert(inputs_bufs.size() == getNumberOfInputSockets()); + Vector<NodeOperationOutput *> orig_links(inputs_bufs.size()); + for (int i = 0; i < inputs_bufs.size(); i++) { + NodeOperationInput *input_socket = getInputSocket(i); + BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); + orig_links[i] = input_socket->getLink(); + input_socket->setLink(buffer_op->getOutputSocket()); + } + return orig_links; +} + +void NodeOperation::remove_buffers_and_restore_original_inputs( + Span<NodeOperationOutput *> original_inputs_links) +{ + BLI_assert(original_inputs_links.size() == getNumberOfInputSockets()); + for (int i = 0; i < original_inputs_links.size(); i++) { + NodeOperation *buffer_op = get_input_operation(i); + BLI_assert(buffer_op != nullptr); + BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); + NodeOperationInput *input_socket = getInputSocket(i); + input_socket->setLink(original_inputs_links[i]); + delete buffer_op; + } +} + +/** \} */ + /***************** **** OpInput **** *****************/ @@ -267,6 +440,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (!node_operation_flags.use_datatype_conversion) { os << "no_conversion,"; } + if (node_operation_flags.is_fullframe_operation) { + os << "full_frame,"; + } return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index baf3a0878b9..11b293a400f 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -39,6 +39,7 @@ namespace blender::compositor { class OpenCLDevice; class ReadBufferOperation; class WriteBufferOperation; +class ExecutionSystem; class NodeOperation; typedef NodeOperation SocketReader; @@ -190,6 +191,10 @@ struct NodeOperationFlags { */ bool open_cl : 1; + /** + * TODO: Remove this flag and #SingleThreadedOperation if tiled implementation is removed. + * Full-frame implementation doesn't need it. + */ bool single_threaded : 1; /** @@ -232,6 +237,11 @@ struct NodeOperationFlags { */ bool use_datatype_conversion : 1; + /** + * Has this operation fullframe implementation. + */ + bool is_fullframe_operation : 1; + NodeOperationFlags() { complex = false; @@ -247,6 +257,7 @@ struct NodeOperationFlags { is_viewer_operation = false; is_preview_operation = false; use_datatype_conversion = true; + is_fullframe_operation = false; } }; @@ -341,6 +352,13 @@ class NodeOperation { NodeOperationOutput *getOutputSocket(unsigned int index = 0); NodeOperationInput *getInputSocket(unsigned int index); + NodeOperation *get_input_operation(int index) + { + /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing + * this method. */ + return getInputOperation(index); + } + /** * \brief determine the resolution of this node * \note this method will not set the resolution, this is the responsibility of the caller @@ -537,6 +555,33 @@ class NodeOperation { return std::unique_ptr<MetaData>(); } + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system); + + /** + * Executes operation updating output memory buffer. Single-threaded calls. + */ + virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_area), + Span<MemoryBuffer *> UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system)) + { + } + + /** + * Get input operation area being read by this operation on rendering given output area. + */ + virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area); + void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area); + + /** \} */ + protected: NodeOperation(); @@ -616,6 +661,27 @@ class NodeOperation { { } + private: + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render_full_frame(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs_bufs, + ExecutionSystem &exec_system); + + void render_full_frame_fallback(MemoryBuffer *output_buf, + Span<rcti> areas, + Span<MemoryBuffer *> inputs, + ExecutionSystem &exec_system); + void render_tile(MemoryBuffer *output_buf, rcti *tile_rect); + Vector<NodeOperationOutput *> replace_inputs_with_buffers(Span<MemoryBuffer *> inputs_bufs); + void remove_buffers_and_restore_original_inputs( + Span<NodeOperationOutput *> original_inputs_links); + + /** \} */ + /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 82eb969b752..c81a5a2bd98 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -99,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) determineResolutions(); - /* surround complex ops with read/write buffer */ - add_complex_operation_buffers(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* surround complex ops with read/write buffer */ + add_complex_operation_buffers(); + } /* links not available from here on */ /* XXX make m_links a local variable to avoid confusion! */ @@ -111,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) /* ensure topological (link-based) order of nodes */ /*sort_operations();*/ /* not needed yet */ - /* create execution groups */ - group_operations(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* create execution groups */ + group_operations(); + } /* transfer resulting operations to the system */ system->set_operations(m_operations, m_groups); diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc new file mode 100644 index 00000000000..4ce674a1c25 --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -0,0 +1,128 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_SharedOperationBuffers.h" +#include "BLI_rect.h" +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +SharedOperationBuffers::BufferData::BufferData() + : buffer(nullptr), registered_reads(0), received_reads(0) +{ +} + +SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op) +{ + return buffers_.lookup_or_add_cb(op, []() { return BufferData(); }); +} + +/** + * Whether given operation area to render is already registered. + * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial + * overlapping, etc. Leading to more rendering than necessary. + */ +bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render) +{ + BufferData &buf_data = get_buffer_data(op); + for (rcti ®_rect : buf_data.render_areas) { + if (BLI_rcti_inside_rcti(®_rect, &area_to_render)) { + return true; + } + } + return false; +} + +/** + * Registers an operation area to render. + */ +void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render) +{ + get_buffer_data(op).render_areas.append(area_to_render); +} + +/** + * Whether given operation has any registered reads (other operation registered it depends on given + * operation). + */ +bool SharedOperationBuffers::has_registered_reads(NodeOperation *op) +{ + return get_buffer_data(op).registered_reads > 0; +} + +/** + * Registers an operation read (other operation depends on given operation). + */ +void SharedOperationBuffers::register_read(NodeOperation *read_op) +{ + get_buffer_data(read_op).registered_reads++; +} + +/** + * Get registered areas given operation needs to render. + */ +blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op) +{ + return get_buffer_data(op).render_areas.as_span(); +} + +/** + * Whether this operation buffer has already been rendered. + */ +bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op) +{ + return get_buffer_data(op).buffer != nullptr; +} + +/** + * Stores given operation rendered buffer. + */ +void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op, + std::unique_ptr<MemoryBuffer> buffer) +{ + BufferData &buf_data = get_buffer_data(op); + BLI_assert(buf_data.received_reads == 0); + BLI_assert(buf_data.buffer == nullptr); + buf_data.buffer = std::move(buffer); +} + +/** + * Get given operation rendered buffer. + */ +MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op) +{ + BLI_assert(is_operation_rendered(op)); + return get_buffer_data(op).buffer.get(); +} + +/** + * Reports an operation has finished reading given operation. If all given operation dependencies + * have finished its buffer will be disposed. + */ +void SharedOperationBuffers::read_finished(NodeOperation *read_op) +{ + BufferData &buf_data = get_buffer_data(read_op); + buf_data.received_reads++; + BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads); + if (buf_data.received_reads == buf_data.registered_reads) { + /* Dispose buffer. */ + buf_data.buffer = nullptr; + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h new file mode 100644 index 00000000000..480a799d89f --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_span.hh" +#include "BLI_vector.hh" +#include "COM_MemoryBuffer.h" +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif +#include <memory> + +namespace blender::compositor { + +/** + * Stores and shares operations rendered buffers including render data. Buffers are + * disposed once all dependent operations have finished reading them. + */ +class SharedOperationBuffers { + private: + typedef struct BufferData { + public: + BufferData(); + std::unique_ptr<MemoryBuffer> buffer; + blender::Vector<rcti> render_areas; + int registered_reads; + int received_reads; + } BufferData; + blender::Map<NodeOperation *, BufferData> buffers_; + + public: + bool is_area_registered(NodeOperation *op, const rcti &area_to_render); + void register_area(NodeOperation *op, const rcti &area_to_render); + + bool has_registered_reads(NodeOperation *op); + void register_read(NodeOperation *read_op); + + blender::Span<rcti> get_areas_to_render(NodeOperation *op); + bool is_operation_rendered(NodeOperation *op); + void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer); + MemoryBuffer *get_rendered_buffer(NodeOperation *op); + + void read_finished(NodeOperation *read_op); + + private: + BufferData &get_buffer_data(NodeOperation *op); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc new file mode 100644 index 00000000000..d025ce53330 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_TiledExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +TiledExecutionModel::TiledExecutionModel(CompositorContext &context, + Span<NodeOperation *> operations, + Span<ExecutionGroup *> groups) + : ExecutionModel(context, operations), groups_(groups) +{ + const bNodeTree *node_tree = context.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution")); + + unsigned int resolution[2]; + for (ExecutionGroup *group : groups_) { + resolution[0] = 0; + resolution[1] = 0; + group->determineResolution(resolution); + + if (border_.use_render_border) { + const rctf *render_border = border_.viewer_border; + group->setRenderBorder( + render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); + } + + if (border_.use_viewer_border) { + const rctf *viewer_border = border_.viewer_border; + group->setViewerBorder( + viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); + } + } +} + +static void update_read_buffer_offset(Span<NodeOperation *> operations) +{ + unsigned int order = 0; + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; + readOperation->setOffset(order); + order++; + } + } +} + +static void init_write_operations_for_execution(Span<NodeOperation *> operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void link_write_buffers(Span<NodeOperation *> operations) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation); + readOperation->updateMemoryBuffer(); + } + } +} + +static void init_non_write_operations_for_execution(Span<NodeOperation *> operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (!operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void init_execution_groups_for_execution(Span<ExecutionGroup *> groups, + const int chunk_size) +{ + for (ExecutionGroup *execution_group : groups) { + execution_group->setChunksize(chunk_size); + execution_group->initExecution(); + } +} + +void TiledExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *editingtree = this->context_.getbNodeTree(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); + + update_read_buffer_offset(operations_); + + init_write_operations_for_execution(operations_, context_.getbNodeTree()); + link_write_buffers(operations_); + init_non_write_operations_for_execution(operations_, context_.getbNodeTree()); + init_execution_groups_for_execution(groups_, context_.getChunksize()); + + WorkScheduler::start(context_); + execute_groups(eCompositorPriority::High, exec_system); + if (!context_.isFastCalculation()) { + execute_groups(eCompositorPriority::Medium, exec_system); + execute_groups(eCompositorPriority::Low, exec_system); + } + WorkScheduler::finish(); + WorkScheduler::stop(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); + + for (NodeOperation *operation : operations_) { + operation->deinitExecution(); + } + + for (ExecutionGroup *execution_group : groups_) { + execution_group->deinitExecution(); + } +} + +void TiledExecutionModel::execute_groups(eCompositorPriority priority, + ExecutionSystem &exec_system) +{ + for (ExecutionGroup *execution_group : groups_) { + if (execution_group->get_flags().is_output && + execution_group->getRenderPriority() == priority) { + execution_group->execute(&exec_system); + } + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h new file mode 100644 index 00000000000..05a795b9f07 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class ExecutionGroup; + +/** + * Operations are executed from outputs to inputs grouped in execution groups and rendered in + * tiles. + */ +class TiledExecutionModel : public ExecutionModel { + private: + Span<ExecutionGroup *> groups_; + + public: + TiledExecutionModel(CompositorContext &context, + Span<NodeOperation *> operations, + Span<ExecutionGroup *> groups); + + void execute(ExecutionSystem &exec_system) override; + + private: + void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 28aa746fdc4..4d503022120 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -22,6 +22,7 @@ #include "BLI_rect.h" +#include <functional> #include <ostream> namespace blender::compositor { @@ -33,6 +34,8 @@ class ExecutionGroup; * \see WorkScheduler */ struct WorkPackage { + eWorkPackageType type; + eWorkPackageState state = eWorkPackageState::NotScheduled; /** @@ -50,6 +53,16 @@ struct WorkPackage { */ rcti rect; + /** + * Custom function to execute when work package type is CustomFunction. + */ + std::function<void()> execute_fn; + + /** + * Called when work execution is finished. + */ + std::function<void()> executed_fn; + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage") #endif diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index d578ac24a4a..157ded943d6 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -98,6 +98,8 @@ static struct { bool active = false; bool initialized = false; } opencl; + + int num_cpu_threads; } g_work_scheduler; /* -------------------------------------------------------------------- */ @@ -143,7 +145,8 @@ static void opencl_start(CompositorContext &context) static bool opencl_schedule(WorkPackage *package) { - if (package->execution_group->get_flags().open_cl && g_work_scheduler.opencl.active) { + if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl && + g_work_scheduler.opencl.active) { BLI_thread_queue_push(g_work_scheduler.opencl.queue, package); return true; } @@ -532,11 +535,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads) opencl_initialize(use_opencl); } + g_work_scheduler.num_cpu_threads = num_cpu_threads; switch (COM_threading_model()) { case ThreadingModel::SingleThreaded: + g_work_scheduler.num_cpu_threads = 1; /* Nothing to do. */ break; - case ThreadingModel::Queue: threading_model_queue_initialize(num_cpu_threads); break; @@ -568,6 +572,11 @@ void WorkScheduler::deinitialize() } } +int WorkScheduler::get_num_cpu_threads() +{ + return g_work_scheduler.num_cpu_threads; +} + int WorkScheduler::current_thread_id() { if (COM_threading_model() == ThreadingModel::SingleThreaded) { diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 85b1d7e2ebf..be88859be7c 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -87,6 +87,8 @@ struct WorkScheduler { */ static bool has_gpu_devices(); + static int get_num_cpu_threads(); + static int current_thread_id(); #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index d97e4371dc6..4032a655633 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -77,10 +77,10 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, /** \name Cryptomatte V2 * \{ */ -static std::string prefix_from_node(const bNode &node) +static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; - ntreeCompositCryptomatteLayerPrefix(&node, prefix, sizeof(prefix)); + ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix)); return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix))); } @@ -118,9 +118,9 @@ void CryptomatteNode::input_operations_from_render_source( return; } - const short cryptomatte_layer_id = 0; - const std::string prefix = prefix_from_node(node); - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + short view_layer_id = 0; + const std::string prefix = prefix_from_node(context, node); + LISTBASE_FOREACH_INDEX (ViewLayer *, view_layer, &scene->view_layers, view_layer_id) { RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { @@ -129,7 +129,7 @@ void CryptomatteNode::input_operations_from_render_source( RenderLayersProg *op = new RenderLayersProg( render_pass->name, DataType::Color, render_pass->channels); op->setScene(scene); - op->setLayerId(cryptomatte_layer_id); + op->setLayerId(view_layer_id); op->setRenderData(context.getRenderData()); op->setViewName(context.getViewName()); r_input_operations.append(op); @@ -177,7 +177,7 @@ void CryptomatteNode::input_operations_from_image_source( } } - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); int layer_index; LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) { if (!blender::StringRef(prefix).startswith(blender::StringRef( diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 922393f006a..1b2ce341a66 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -56,7 +56,10 @@ void TranslateNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); - if (data->wrap_axis) { + /* FullFrame does not support using WriteBufferOperation. + * TODO: Implement TranslateOperation with wrap support in FullFrame. + */ + if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); WrapOperation *wrapOperation = new WrapOperation(DataType::Color); wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 2ea15185c0f..384936533c7 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -18,6 +18,8 @@ #include "COM_ConvertOperation.h" +#include "BLI_color.hh" + #include "IMB_colormanagement.h" namespace blender::compositor { @@ -355,21 +357,10 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - float inputValue[4]; - float alpha; - - this->m_inputOperation->readSampled(inputValue, x, y, sampler); - alpha = inputValue[3]; - - if (fabsf(alpha) < 1e-5f) { - zero_v3(output); - } - else { - mul_v3_v3fl(output, inputValue, 1.0f / alpha); - } - - /* never touches the alpha */ - output[3] = alpha; + ColorSceneLinear4f<eAlpha::Premultiplied> input; + this->m_inputOperation->readSampled(input, x, y, sampler); + ColorSceneLinear4f<eAlpha::Straight> converted = input.unpremultiply_alpha(); + copy_v4_v4(output, converted); } /* ******** Straight to Premul ******** */ @@ -385,16 +376,10 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - float inputValue[4]; - float alpha; - - this->m_inputOperation->readSampled(inputValue, x, y, sampler); - alpha = inputValue[3]; - - mul_v3_v3fl(output, inputValue, alpha); - - /* never touches the alpha */ - output[3] = alpha; + ColorSceneLinear4f<eAlpha::Straight> input; + this->m_inputOperation->readSampled(input, x, y, sampler); + ColorSceneLinear4f<eAlpha::Premultiplied> converted = input.premultiply_alpha(); + copy_v4_v4(output, converted); } /* ******** Separate Channels ******** */ diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 46ae00dee34..4edcc206f5b 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -31,6 +31,31 @@ namespace blender::compositor { +PlaneDistortBaseOperation::PlaneDistortBaseOperation() + : m_motion_blur_samples(1), m_motion_blur_shutter(0.5f) +{ +} + +void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2], + bool normalized, + int sample) +{ + BLI_assert(sample < this->m_motion_blur_samples); + MotionSample *sample_data = &this->m_samples[sample]; + if (normalized) { + for (int i = 0; i < 4; i++) { + sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); + sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); + } + } + else { + for (int i = 0; i < 4; i++) { + sample_data->frameSpaceCorners[i][0] = corners[i][0]; + sample_data->frameSpaceCorners[i][1] = corners[i][1]; + } + } +} + /* ******** PlaneDistort WarpImage ******** */ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2]) @@ -46,13 +71,11 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo deriv[1][1] = (matrix[1][1] - matrix[1][2] * uv[1]) / vec[2]; } -PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() +PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation() { this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->m_pixelReader = nullptr; - this->m_motion_blur_samples = 1; - this->m_motion_blur_shutter = 0.5f; this->flags.complex = true; } @@ -60,24 +83,13 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], bool normalized, int sample) { - BLI_assert(sample < this->m_motion_blur_samples); + PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample); + const int width = this->m_pixelReader->getWidth(); const int height = this->m_pixelReader->getHeight(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; MotionSample *sample_data = &this->m_samples[sample]; - if (normalized) { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); - } - } - else { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0]; - sample_data->frameSpaceCorners[i][1] = corners[i][1]; - } - } BKE_tracking_homography_between_two_quads( sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix); } @@ -147,34 +159,12 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( /* ******** PlaneDistort Mask ******** */ -PlaneDistortMaskOperation::PlaneDistortMaskOperation() +PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation() { addOutputSocket(DataType::Value); /* Currently hardcoded to 8 samples. */ m_osa = 8; - this->m_motion_blur_samples = 1; - this->m_motion_blur_shutter = 0.5f; -} - -void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2], - bool normalized, - int sample) -{ - BLI_assert(sample < this->m_motion_blur_samples); - MotionSample *sample_data = &this->m_samples[sample]; - if (normalized) { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); - } - } - else { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0]; - sample_data->frameSpaceCorners[i][1] = corners[i][1]; - } - } } void PlaneDistortMaskOperation::initExecution() diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index 95e5c86bd4d..cc6e4d00d71 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -32,21 +32,43 @@ namespace blender::compositor { #define PLANE_DISTORT_MAX_SAMPLES 64 -class PlaneDistortWarpImageOperation : public NodeOperation { +class PlaneDistortBaseOperation : public NodeOperation { protected: struct MotionSample { float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ float perspectiveMatrix[3][3]; }; - SocketReader *m_pixelReader; MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; int m_motion_blur_samples; float m_motion_blur_shutter; public: + PlaneDistortBaseOperation(); + + void setMotionBlurSamples(int samples) + { + BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); + this->m_motion_blur_samples = samples; + } + void setMotionBlurShutter(float shutter) + { + this->m_motion_blur_shutter = shutter; + } + + virtual void calculateCorners(const float corners[4][2], bool normalized, int sample); + + private: + friend class PlaneTrackCommon; +}; + +class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { + protected: + SocketReader *m_pixelReader; + + public: PlaneDistortWarpImageOperation(); - void calculateCorners(const float corners[4][2], bool normalized, int sample); + void calculateCorners(const float corners[4][2], bool normalized, int sample) override; void initExecution() override; void deinitExecution() override; @@ -56,47 +78,19 @@ class PlaneDistortWarpImageOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - - void setMotionBlurSamples(int samples) - { - BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - this->m_motion_blur_samples = samples; - } - void setMotionBlurShutter(float shutter) - { - this->m_motion_blur_shutter = shutter; - } }; -class PlaneDistortMaskOperation : public NodeOperation { +class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { protected: - struct MotionSample { - float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ - }; int m_osa; - MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; float m_jitter[32][2]; - int m_motion_blur_samples; - float m_motion_blur_shutter; public: PlaneDistortMaskOperation(); - void calculateCorners(const float corners[4][2], bool normalized, int sample); - void initExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - - void setMotionBlurSamples(int samples) - { - BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - this->m_motion_blur_samples = samples; - } - void setMotionBlurShutter(float shutter) - { - this->m_motion_blur_shutter = shutter; - } }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 565bde6c945..0884f2ad979 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -41,6 +41,26 @@ PlaneTrackCommon::PlaneTrackCommon() this->m_planeTrackName[0] = '\0'; } +void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op) +{ + float corners[4][2]; + if (distort_op->m_motion_blur_samples == 1) { + readCornersFromTrack(corners, this->m_framenumber); + distort_op->calculateCorners(corners, true, 0); + } + else { + const float frame = (float)this->m_framenumber - distort_op->m_motion_blur_shutter; + const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) / + distort_op->m_motion_blur_samples; + float frame_iter = frame; + for (int sample = 0; sample < distort_op->m_motion_blur_samples; sample++) { + readCornersFromTrack(corners, frame_iter); + distort_op->calculateCorners(corners, true, sample); + frame_iter += frame_step; + } + } +} + void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) { MovieTracking *tracking; @@ -84,21 +104,7 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], void PlaneTrackMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); - float corners[4][2]; - if (this->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, this->m_framenumber); - calculateCorners(corners, true, 0); - } - else { - const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter; - const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples; - float frame_iter = frame; - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { - readCornersFromTrack(corners, frame_iter); - calculateCorners(corners, true, sample); - frame_iter += frame_step; - } - } + PlaneTrackCommon::read_and_calculate_corners(this); } /* ******** PlaneTrackWarpImageOperation ******** */ @@ -106,22 +112,7 @@ void PlaneTrackMaskOperation::initExecution() void PlaneTrackWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); - /* TODO(sergey): De-duplicate with mask operation. */ - float corners[4][2]; - if (this->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, this->m_framenumber); - calculateCorners(corners, true, 0); - } - else { - const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter; - const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples; - float frame_iter = frame; - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { - readCornersFromTrack(corners, frame_iter); - calculateCorners(corners, true, sample); - frame_iter += frame_step; - } - } + PlaneTrackCommon::read_and_calculate_corners(this); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 95a7d536742..d240c8b06e9 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -40,7 +40,7 @@ class PlaneTrackCommon { /* note: this class is not an operation itself (to prevent virtual inheritance issues) * implementation classes must make wrappers to use these methods, see below. */ - void readCornersFromTrack(float corners[4][2], float frame); + void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op); void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); public: @@ -62,6 +62,9 @@ class PlaneTrackCommon { { this->m_framenumber = framenumber; } + + private: + void readCornersFromTrack(float corners[4][2], float frame); }; class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon { diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index e94c457f981..7517ff8a137 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -75,16 +75,13 @@ void TextureBaseOperation::deinitExecution() void TextureBaseOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { - if (preferredResolution[0] == 0 || preferredResolution[1] == 0) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; - resolution[0] = width; - resolution[1] = height; - } - else { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; - } + /* Determine inputs resolutions. */ + unsigned int temp[2]; + NodeOperation::determineResolution(temp, preferredResolution); + + /* We don't use inputs resolutions because they are only used as parameters, not image data. */ + resolution[0] = preferredResolution[0]; + resolution[1] = preferredResolution[1]; } void TextureAlphaOperation::executePixelSampled(float output[4], diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index e1e04611c6c..e5f56673694 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -44,7 +44,7 @@ class TextureBaseOperation : public NodeOperation { protected: /** - * Determine the output resolution. The resolution is retrieved from the Renderer + * Determine the output resolution. */ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 5f9e78837a7..b4acf9b010c 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -189,6 +189,8 @@ void DEG_add_customdata_mask(struct DepsNodeHandle *handle, struct ID *DEG_get_id_from_handle(struct DepsNodeHandle *node_handle); struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle); +bool DEG_object_has_geometry_component(struct Object *object); + /* ************************************************ */ #ifdef __cplusplus diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 77661b4870a..ae530cc010e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -427,9 +427,9 @@ static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackD * * NOTE: Currently the only ID types that depsgraph may decide to not evaluate/generate COW * copies for, even though they are referenced by other data-blocks, are Collections and Objects - * (through their various visbility flags, and the ones from LayerCollections too). However, this + * (through their various visibility flags, and the ones from LayerCollections too). However, this * code is kept generic as it makes it more future-proof, and optimization here would give - * neglectable performance improvements in typical cases. + * negligible performance improvements in typical cases. * * NOTE: This mechanism may also 'fix' some missing update tagging from non-depsgraph code in * some cases. This is slightly unfortunate (as it may hide issues in other parts of Blender diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 6c1e91d068b..9e9191c5ab9 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -62,6 +62,7 @@ #include "intern/depsgraph_registry.h" #include "intern/depsgraph_relation.h" +#include "intern/depsgraph_tag.h" #include "intern/depsgraph_type.h" /* ****************** */ @@ -109,6 +110,11 @@ void DEG_add_object_relation(DepsNodeHandle *node_handle, deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description); } +bool DEG_object_has_geometry_component(Object *object) +{ + return deg::geometry_tag_to_component(&object->id) != deg::NodeType::UNDEFINED; +} + void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle, Collection *collection, const char *description) diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index ed002321729..df1cf8cc771 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -219,6 +219,29 @@ bool deg_iterator_components_step(BLI_Iterator *iter) } } + /* The curve component. */ + if (data->geometry_component_id == 3) { + data->geometry_component_id++; + + const CurveComponent *component = geometry_set->get_component_for_read<CurveComponent>(); + if (component != nullptr) { + const Curve *curve = component->get_curve_for_render(); + + if (curve != nullptr) { + Object *temp_object = &data->temp_geometry_component_object; + *temp_object = *data->geometry_component_owner; + temp_object->type = OB_CURVE; + temp_object->data = (void *)curve; + /* Assign data_eval here too, because curve rendering code tries + * to use a mesh if it can find one in this pointer. */ + temp_object->runtime.data_eval = (ID *)curve; + temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; + iter->current = temp_object; + return true; + } + } + } + data->geometry_component_owner = nullptr; return false; } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 045adf4b380..ee5b2c549a5 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC ../depsgraph ../editors/include ../editors/space_view3d + ../functions ../gpu ../imbuf ../makesdna @@ -50,7 +51,9 @@ set(INC set(SRC intern/draw_cache.c - intern/draw_cache_extract_mesh.c + intern/draw_cache_extract_mesh_extractors.c + intern/draw_cache_extract_mesh_render_data.c + intern/draw_cache_extract_mesh.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_displist.c intern/draw_cache_impl_gpencil.c @@ -146,6 +149,7 @@ set(SRC engines/overlay/overlay_image.c engines/overlay/overlay_lattice.c engines/overlay/overlay_metaball.c + engines/overlay/overlay_mode_transfer.c engines/overlay/overlay_motion_path.c engines/overlay/overlay_outline.c engines/overlay/overlay_paint.c @@ -321,6 +325,7 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC) data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC) +data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC) data_to_c_simple(intern/shaders/common_math_lib.glsl SRC) data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC) data_to_c_simple(intern/shaders/common_view_lib.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index cba86d058ea..e23a5a81169 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -96,7 +96,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli) } } else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { - power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* 1/(4*r²*Pi²) */ + power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */ /* for point lights (a.k.a radius == 0.0) */ // power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */ @@ -106,7 +106,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli) /* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we * cannot reproduce so we account for that by scaling the light power. This function is the * result of a rough manual fitting. */ - power += 1.0f / (2.0f * M_PI); /* power *= 1 + r²/2 */ + power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */ } return power; } @@ -257,7 +257,7 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) float power = max_fff(UNPACK3(evli->color)) * evli->volume; if (power > 0.0f && evli->light_type != LA_SUN) { /* The limit of the power attenuation function when the distance to the light goes to 0 is - * 2 / r² where r is the light radius. We need to find the right radius that emits at most + * `2 / r^2` where r is the light radius. We need to find the right radius that emits at most * the volume light upper bound. Inverting the function we get: */ float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power); /* Square it here to avoid a multiplication inside the shader. */ diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 5739024993e..5ada53ab98c 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -79,7 +79,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) * type the rest of the bits are used for the name hash. */ int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) { - int hash = BLI_hash_string(aov->name); + int hash = BLI_hash_string(aov->name) << 1; SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); return hash; } diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index fdbb70e917d..05496ad4ab0 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -138,30 +138,55 @@ void accumulate_light(vec3 light, float fac, inout vec4 accum) /* Same thing as Cycles without the comments to make it shorter. */ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) { - vec3 R; - float NI = dot(N, I); - float NgR, threshold; - /* Check if the incident ray is coming from behind normal N. */ - if (NI > 0.0) { - /* Normal reflection. */ - R = (2.0 * NI) * N - I; - NgR = dot(Ng, R); - /* Reflection rays may always be at least as shallow as the incoming ray. */ - threshold = min(0.9 * dot(Ng, I), 0.01); - if (NgR >= threshold) { - return N; + vec3 R = -reflect(I, N); + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9 * dot(Ng, I), 0.025); + if (dot(Ng, R) >= threshold) { + return N; + } + + float NdotNg = dot(N, Ng); + vec3 X = normalize(N - NdotNg * Ng); + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = sqrt(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + float fac = 0.5 / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); + bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); + + vec2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2)); + vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2)); + + float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5); + valid2 = (R2 >= 1e-5); + if (valid1 && valid2) { + N_new = (R1 < R2) ? N1 : N2; } + else { + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2)); } else { - /* Bad incident. */ - R = -I; - NgR = dot(Ng, R); - threshold = 0.01; + return Ng; } - /* Lift the reflection above the threshold. */ - R = R + Ng * (threshold - NgR); - /* Find a bisector. */ - return safe_normalize(I * length(R) + R * length(I)); + return N_new.x * X + N_new.y * Ng; } /* ----------- Cone angle Approximation --------- */ diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index adb70f97585..af8b029a08e 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -274,7 +274,13 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, const bool override_vertcol = (pd->v3d_color_type != -1); const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) || GPENCIL_VERTEX_MODE(gpd) || pd->is_render; - bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers); + const bool is_viewlayer_render = pd->is_render && (gpl->viewlayername[0] != '\0') && + STREQ(pd->view_layer->name, gpl->viewlayername); + const bool disable_masks_render = is_viewlayer_render && + (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); + bool is_masked = disable_masks_render ? false : + (gpl->flag & GP_LAYER_USE_MASK) && + !BLI_listbase_is_empty(&gpl->mask_layers); float vert_col_opacity = (override_vertcol) ? (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) : diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 5ceb909bc88..34fe29055d6 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -297,7 +297,9 @@ typedef struct GPENCIL_PrivateData { int v3d_color_type; /* Current frame */ int cfra; - /* If we are rendering for final render (F12). */ + /* If we are rendering for final render (F12). + * NOTE: set to false for viewport and opengl rendering (including VSE scene rendering), but set + * to true when rendering in `OB_RENDER` shading mode (viewport or opengl rendering) */ bool is_render; /* If we are in viewport display (used for VFX). */ bool is_viewport; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7412959a30b..ac48b94fea9 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,6 +442,10 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; + /* For one point strokes use object aligment. */ + if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { + alignement = GP_STROKE_ALIGNMENT_OBJECT; + } # endif vec2 x_axis; diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index 40a9dbe01c0..7639911286f 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -149,7 +149,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) DRWState state_common = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; /* Faces */ - /* Cage geom needs to be offsetted to avoid Z-fighting. */ + /* Cage geom needs an offset applied to avoid Z-fighting. */ for (int j = 0; j < 2; j++) { DRWPass **edit_face_ps = (j == 0) ? &psl->edit_mesh_faces_ps[i] : &psl->edit_mesh_faces_cage_ps[i]; @@ -197,7 +197,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) grp = pd->edit_mesh_skin_roots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); } - /* Facedots */ + /* Face-dots */ if (select_face && show_face_dots) { sh = OVERLAY_shader_edit_mesh_facedot(); grp = pd->edit_mesh_facedots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b8f721946f2..19f822e3f68 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -207,6 +207,7 @@ static void OVERLAY_cache_init(void *vedata) OVERLAY_armature_cache_init(vedata); OVERLAY_background_cache_init(vedata); OVERLAY_fade_cache_init(vedata); + OVERLAY_mode_transfer_cache_init(vedata); OVERLAY_extra_cache_init(vedata); OVERLAY_facing_cache_init(vedata); OVERLAY_gpencil_cache_init(vedata); @@ -323,6 +324,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) !is_select; const bool draw_fade = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FADE_INACTIVE) && overlay_should_fade_object(ob, draw_ctx->obact); + const bool draw_mode_transfer = draw_surface && (pd->overlay.flag & V3D_OVERLAY_MODE_TRANSFER); const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0; const bool draw_wires = draw_surface && has_surface && (pd->wireframe_mode || !pd->hide_overlays); @@ -349,6 +351,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) if (draw_facing) { OVERLAY_facing_cache_populate(vedata, ob); } + if (draw_mode_transfer) { + OVERLAY_mode_transfer_cache_populate(vedata, ob); + } if (draw_wires) { OVERLAY_wireframe_cache_populate(vedata, ob, dupli, do_init); } @@ -504,6 +509,7 @@ static void OVERLAY_cache_finish(void *vedata) {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); } + OVERLAY_mode_transfer_cache_finish(vedata); OVERLAY_antialiasing_cache_finish(vedata); OVERLAY_armature_cache_finish(vedata); OVERLAY_image_cache_finish(vedata); @@ -566,6 +572,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_image_draw(vedata); OVERLAY_fade_draw(vedata); OVERLAY_facing_draw(vedata); + OVERLAY_mode_transfer_draw(vedata); OVERLAY_extra_blend_draw(vedata); OVERLAY_volume_draw(vedata); @@ -605,6 +612,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_fade_infront_draw(vedata); OVERLAY_facing_infront_draw(vedata); + OVERLAY_mode_transfer_infront_draw(vedata); if (DRW_state_is_fbo()) { GPU_framebuffer_bind(fbl->overlay_line_in_front_fb); diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.c b/source/blender/draw/engines/overlay/overlay_mode_transfer.c new file mode 100644 index 00000000000..253f606b086 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.c @@ -0,0 +1,159 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "BKE_paint.h" +#include "DRW_render.h" + +#include "ED_view3d.h" + +#include "PIL_time.h" +#include "UI_resources.h" + +#include "overlay_private.h" + +void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + pd->mode_transfer.time = PIL_check_seconds_timer(); + + for (int i = 0; i < 2; i++) { + /* Non Meshes Pass (Camera, empties, lights ...) */ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->mode_transfer_ps[i], state | pd->clipping_state); + } +} + +#define MODE_TRANSFER_FLASH_LENGTH 0.55f +/* TODO(pablodp606): Remove this option for 3.0 if fade in/out is not used. */ +#define MODE_TRANSFER_FLASH_FADE 0.0f +#define MODE_TRANSFER_FLASH_MAX_ALPHA 0.25f + +static bool mode_transfer_is_animation_running(const float anim_time) +{ + return anim_time >= 0.0f && anim_time <= MODE_TRANSFER_FLASH_LENGTH; +} + +static float mode_transfer_alpha_for_animation_time_get(const float anim_time) +{ + if (anim_time > MODE_TRANSFER_FLASH_LENGTH) { + return 0.0f; + } + + if (anim_time < 0.0f) { + return 0.0f; + } + + if (MODE_TRANSFER_FLASH_FADE <= 0.0f) { + return (1.0f - (anim_time / MODE_TRANSFER_FLASH_LENGTH)) * MODE_TRANSFER_FLASH_MAX_ALPHA; + } + + const float flash_fade_in_time = MODE_TRANSFER_FLASH_LENGTH * MODE_TRANSFER_FLASH_FADE; + const float flash_fade_out_time = MODE_TRANSFER_FLASH_LENGTH - flash_fade_in_time; + + float alpha = 0.0f; + if (anim_time < flash_fade_in_time) { + alpha = anim_time / flash_fade_in_time; + } + else { + const float fade_out_anim_time = anim_time - flash_fade_in_time; + alpha = 1.0f - (fade_out_anim_time / flash_fade_out_time); + } + + return alpha * MODE_TRANSFER_FLASH_MAX_ALPHA; +} + +void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_PassList *psl = vedata->psl; + + if (pd->xray_enabled) { + return; + } + + const float animation_time = pd->mode_transfer.time - + ob->runtime.overlay_mode_transfer_start_time; + + if (!mode_transfer_is_animation_running(animation_time)) { + return; + } + + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && + !DRW_state_is_image_render(); + const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0; + + DRWShadingGroup *mode_transfer_grp[2]; + + for (int i = 0; i < 2; i++) { + GPUShader *sh = OVERLAY_shader_uniform_color(); + mode_transfer_grp[i] = DRW_shgroup_create(sh, psl->mode_transfer_ps[i]); + DRW_shgroup_uniform_block(mode_transfer_grp[i], "globalsBlock", G_draw.block_ubo); + + float color[4]; + UI_GetThemeColor3fv(TH_VERTEX_SELECT, color); + color[3] = mode_transfer_alpha_for_animation_time_get(animation_time); + srgb_to_linearrgb_v4(color, color); + DRW_shgroup_uniform_vec4_copy(mode_transfer_grp[i], "color", color); + } + + if (!pd->use_in_front) { + mode_transfer_grp[IN_FRONT] = mode_transfer_grp[NOT_IN_FRONT]; + } + + pd->mode_transfer.any_animated = true; + + if (use_sculpt_pbvh) { + DRW_shgroup_call_sculpt(mode_transfer_grp[is_xray], ob, false, false); + } + else { + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRW_shgroup_call(mode_transfer_grp[is_xray], geom, ob); + } + } +} + +void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->mode_transfer_ps[NOT_IN_FRONT]); +} + +void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->mode_transfer_ps[IN_FRONT]); +} + +void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + if (pd->mode_transfer.any_animated) { + DRW_viewport_request_redraw(); + } + pd->mode_transfer.any_animated = false; +} diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c index 48b7b53a5ba..a92f11aca38 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.c +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -190,7 +190,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, bool is_keyframe = (mpv->flag & MOTIONPATH_VERT_KEY) != 0; if ((show_keyframes && show_keyframes_no && is_keyframe) || (show_frame_no && (i == 0))) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame); DRW_text_cache_add( dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, (is_keyframe) ? col_kf : col); } @@ -200,7 +200,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, /* Only draw frame number if several consecutive highlighted points * don't occur on same point. */ if ((equals_v3v3(mpv->co, mpvP->co) == 0) || (equals_v3v3(mpv->co, mpvN->co) == 0)) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame); DRW_text_cache_add(dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, col); } } diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index db43136e308..969289a3219 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -107,6 +107,7 @@ typedef struct OVERLAY_PassList { DRWPass *gpencil_canvas_ps; DRWPass *facing_ps[2]; DRWPass *fade_ps[2]; + DRWPass *mode_transfer_ps[2]; DRWPass *grid_ps; DRWPass *image_background_ps; DRWPass *image_background_scene_ps; @@ -282,6 +283,7 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *extra_grid_grp; DRWShadingGroup *facing_grp[2]; DRWShadingGroup *fade_grp[2]; + DRWShadingGroup *flash_grp[2]; DRWShadingGroup *motion_path_lines_grp; DRWShadingGroup *motion_path_points_grp; DRWShadingGroup *outlines_grp; @@ -414,6 +416,10 @@ typedef struct OVERLAY_PrivateData { struct { DRWCallBuffer *handle[2]; } mball; + struct { + double time; + bool any_animated; + } mode_transfer; } OVERLAY_PrivateData; /* Transient data */ typedef struct OVERLAY_StorageList { @@ -607,6 +613,12 @@ void OVERLAY_fade_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_fade_draw(OVERLAY_Data *vedata); void OVERLAY_fade_infront_draw(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata); + void OVERLAY_grid_init(OVERLAY_Data *vedata); void OVERLAY_grid_cache_init(OVERLAY_Data *vedata); void OVERLAY_grid_draw(OVERLAY_Data *vedata); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 2545cfa65dc..5071658fd82 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -438,6 +438,10 @@ void DRW_shgroup_call_range( void DRW_shgroup_call_instance_range( DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); +void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -575,6 +579,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, const char *name, const float (*value)[4], int arraysize); +void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer); bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index bbb97fc09a3..810deaec349 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "GPU_batch.h" +#include "GPU_index_buffer.h" +#include "GPU_vertex_buffer.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -64,13 +68,13 @@ typedef struct DRW_MeshCDMask { * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") - typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, MR_ITER_LEDGE = 1 << 2, MR_ITER_LVERT = 1 << 3, } eMRIterType; +ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) typedef enum eMRDataType { MR_DATA_POLY_NOR = 1 << 1, @@ -79,12 +83,11 @@ typedef enum eMRDataType { /** Force loop normals calculation. */ MR_DATA_TAN_LOOP_NOR = 1 << 4, } eMRDataType; +ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR) -typedef enum eMRExtractType { - MR_EXTRACT_BMESH, - MR_EXTRACT_MAPPED, - MR_EXTRACT_MESH, -} eMRExtractType; +#ifdef __cplusplus +extern "C" { +#endif BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { @@ -150,6 +153,18 @@ typedef struct MeshBufferCache { GPUIndexBuf **tris_per_mat; } MeshBufferCache; +/** + * Data that are kept around between extractions to reduce rebuilding time. + * + * - Loose geometry. + */ +typedef struct MeshBufferExtractionCache { + int edge_loose_len; + int vert_loose_len; + int *lverts; + int *ledges; +} MeshBufferExtractionCache; + typedef enum DRWBatchFlag { MBC_SURFACE = (1 << 0), MBC_SURFACE_WEIGHTS = (1 << 1), @@ -195,6 +210,10 @@ typedef enum DRWBatchFlag { typedef struct MeshBatchCache { MeshBufferCache final, cage, uv_cage; + MeshBufferExtractionCache final_extraction_cache; + MeshBufferExtractionCache cage_extraction_cache; + MeshBufferExtractionCache uv_cage_extraction_cache; + struct { /* Surfaces / Render */ GPUBatch *surface; @@ -264,9 +283,14 @@ typedef struct MeshBatchCache { bool no_loose_wire; } MeshBatchCache; +#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / sizeof(void *)) +#define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *)) +#define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *)) + void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache mbc, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, const bool is_paint_mode, @@ -275,7 +299,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc new file mode 100644 index 00000000000..fca40206659 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -0,0 +1,1004 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" + +#include "BLI_task.h" +#include "BLI_vector.hh" + +#include "BKE_editmesh.h" + +#include "GPU_capabilities.h" + +#include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_inline.h" + +// #define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + +#define CHUNK_SIZE 8192 + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +struct ExtractorRunData { + /* Extractor where this run data belongs to. */ + const MeshExtract *extractor; + /* During iteration the VBO/IBO that is being build. */ + void *buffer = nullptr; + /* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract + * functions. */ + void *user_data = nullptr; + + ExtractorRunData(const MeshExtract *extractor) : extractor(extractor) + { + } +}; + +class ExtractorRunDatas : public Vector<ExtractorRunData> { + public: + void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + result.append(data); + continue; + } + } + } + + void filter_threaded_extractors_into(ExtractorRunDatas &result) + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if (extractor->use_threading) { + result.append(extractor); + } + } + } + + eMRIterType iter_types() + { + eMRIterType iter_type = static_cast<eMRIterType>(0); + + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + iter_type |= mesh_extract_iter_type(extractor); + } + return iter_type; + } + + eMRDataType data_types() + { + eMRDataType data_type = static_cast<eMRDataType>(0); + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + data_type |= extractor->data_type; + } + return data_type; + } +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract + * \{ */ + +BLI_INLINE void extract_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas &extractors, + MeshBufferCache *mbc) +{ + /* Multi thread. */ + for (ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + run_data.buffer = mesh_extract_buffer_get(extractor, mbc); + run_data.user_data = extractor->init(mr, cache, run_data.buffer); + } +} + +BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, + const ExtractTriBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, + const ExtractTriMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, + const ExtractPolyBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, + const ExtractPolyMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, + const ExtractLEdgeBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, + const ExtractLEdgeMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, + const ExtractLVertBMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, + const ExtractLVertMesh_Params *params, + const ExtractorRunDatas &all_extractors) +{ + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) + { + for (ExtractorRunData &run_data : extractors) { + run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_MESH_END; +} + +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const ExtractorRunDatas &extractors) +{ + for (const ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (extractor->finish) { + extractor->finish(mr, cache, run_data.buffer, run_data.user_data); + } + } +} + +/* Single Thread. */ +BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas &extractors, + eMRIterType iter_type, + MeshBufferCache *mbc) +{ + extract_init(mr, cache, extractors, mbc); + + bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; + if (iter_type & MR_ITER_LOOPTRI) { + if (is_mesh) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + else { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_POLY) { + if (is_mesh) { + ExtractPolyMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + else { + ExtractPolyBMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LEDGE) { + if (is_mesh) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + else { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LVERT) { + if (is_mesh) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + else { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + } + extract_finish(mr, cache, extractors); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name ExtractTaskData + * \{ */ +struct ExtractTaskData { + const MeshRenderData *mr = nullptr; + MeshBatchCache *cache = nullptr; + /* #UserData is shared between the iterations as it holds counters to detect if the + * extraction is finished. To make sure the duplication of the user_data does not create a new + * instance of the counters we allocate the user_data in its own container. + * + * This structure makes sure that when extract_init is called, that the user data of all + * iterations are updated. */ + + ExtractorRunDatas *extractors = nullptr; + MeshBufferCache *mbc = nullptr; + int32_t *task_counter = nullptr; + + eMRIterType iter_type; + int start = 0; + int end = INT_MAX; + /** Decremented each time a task is finished. */ + + ExtractTaskData(const MeshRenderData *mr, + struct MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter) + { + iter_type = extractors->iter_types(); + }; + + ExtractTaskData(const ExtractTaskData &src) = default; + + ~ExtractTaskData() + { + delete extractors; + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData") +#endif +}; + +static ExtractTaskData *extract_extract_iter_task_data_create_mesh(const MeshRenderData *mr, + MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + +{ + ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter); + return taskdata; +} + +static void extract_task_data_free(void *data) +{ + ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); + delete task_data; +} + +static void extract_task_data_free_ex(void *data) +{ + ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data); + task_data->extractors = nullptr; + delete task_data; +} + +BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, + const eMRIterType iter_type, + int start, + int end, + ExtractorRunDatas &extractors) +{ + switch (mr->extract_type) { + case MR_EXTRACT_BMESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyBMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + break; + case MR_EXTRACT_MAPPED: + case MR_EXTRACT_MESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + break; + } +} + +static void extract_task_init(ExtractTaskData *data) +{ + extract_init(data->mr, data->cache, *data->extractors, data->mbc); +} + +static void extract_task_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, *data->extractors); + + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0) { + extract_finish(data->mr, data->cache, *data->extractors); + } +} + +static void extract_task_init_and_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + extract_run_and_finish_init( + data->mr, data->cache, *data->extractors, data->iter_type, data->mbc); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Update Mesh Render Data + * \{ */ +struct MeshRenderDataUpdateTaskData { + MeshRenderData *mr = nullptr; + eMRIterType iter_type; + eMRDataType data_flag; + + ~MeshRenderDataUpdateTaskData() + { + mesh_render_data_free(mr); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData") +#endif +}; + +static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) +{ + BLI_assert(taskdata); + delete taskdata; +} + +static void mesh_extract_render_data_node_exec(void *__restrict task_data) +{ + MeshRenderDataUpdateTaskData *update_task_data = static_cast<MeshRenderDataUpdateTaskData *>( + task_data); + MeshRenderData *mr = update_task_data->mr; + const eMRIterType iter_type = update_task_data->iter_type; + const eMRDataType data_flag = update_task_data->data_flag; + + mesh_render_data_update_normals(mr, data_flag); + mesh_render_data_update_looptris(mr, iter_type, data_flag); +} + +static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, + MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + MeshRenderDataUpdateTaskData *task_data = new (MeshRenderDataUpdateTaskData); + task_data->mr = mr; + task_data->iter_type = iter_type; + task_data->data_flag = data_flag; + + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + mesh_extract_render_data_node_exec, + task_data, + (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Extract Single Threaded + * \{ */ + +static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, + ExtractTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + extract_task_init_and_run, + task_data, + (TaskGraphNodeFreeFunction)extract_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - UserData Initializer + * \{ */ +struct UserDataInitTaskData { + ExtractTaskData *td; + int32_t task_counter = 0; + + ~UserDataInitTaskData() + { + extract_task_data_free(td); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:UserDataInitTaskData") +#endif +}; + +static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) +{ + delete taskdata; +} + +static void user_data_init_task_data_exec(void *__restrict task_data) +{ + UserDataInitTaskData *extract_task_data = static_cast<UserDataInitTaskData *>(task_data); + ExtractTaskData *taskdata_base = extract_task_data->td; + extract_task_init(taskdata_base); +} + +static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, + UserDataInitTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + user_data_init_task_data_exec, + task_data, + (TaskGraphNodeFreeFunction)user_data_init_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop + * \{ */ + +static void extract_range_task_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata, + const eMRIterType type, + int start, + int length) +{ + taskdata = new ExtractTaskData(*taskdata); + atomic_add_and_fetch_int32(taskdata->task_counter, 1); + taskdata->iter_type = type; + taskdata->start = start; + taskdata->end = start + length; + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, extract_task_run, taskdata, extract_task_data_free_ex); + BLI_task_graph_edge_create(task_node_user_data_init, task_node); +} + +static int extract_range_task_num_elements_get(const MeshRenderData *mr, + const eMRIterType iter_type) +{ + /* Divide task into sensible chunks. */ + int iter_len = 0; + if (iter_type & MR_ITER_LOOPTRI) { + iter_len += mr->tri_len; + } + if (iter_type & MR_ITER_POLY) { + iter_len += mr->poly_len; + } + if (iter_type & MR_ITER_LEDGE) { + iter_len += mr->edge_loose_len; + } + if (iter_type & MR_ITER_LVERT) { + iter_len += mr->vert_loose_len; + } + return iter_len; +} + +static int extract_range_task_chunk_size_get(const MeshRenderData *mr, + const eMRIterType iter_type, + const int num_threads) +{ + /* Divide task into sensible chunks. */ + const int num_elements = extract_range_task_num_elements_get(mr, iter_type); + int range_len = (num_elements + num_threads) / num_threads; + CLAMP_MIN(range_len, CHUNK_SIZE); + return range_len; +} + +static void extract_task_in_ranges_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata_base, + const int num_threads) +{ + const MeshRenderData *mr = taskdata_base->mr; + const int range_len = extract_range_task_chunk_size_get( + mr, taskdata_base->iter_type, num_threads); + + if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_POLY) { + for (int i = 0; i < mr->poly_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); + } + } +} + +static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. + * This sub-graph starts with an extract_render_data_node. This fills/converts the required + * data from Mesh. + * + * Small extractions and extractions that can't be multi-threaded are grouped in a single + * `extract_single_threaded_task_node`. + * + * Other extractions will create a node for each loop exceeding 8192 items. these nodes are + * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the + * user_data needed for the extraction based on the data extracted from the mesh. + * counters are used to check if the finalize of a task has to be called. + * + * Mesh extraction sub graph + * + * +----------------------+ + * +-----> | extract_task1_loop_1 | + * | +----------------------+ + * +------------------+ +----------------------+ +----------------------+ + * | mesh_render_data | --> | | --> | extract_task1_loop_2 | + * +------------------+ | | +----------------------+ + * | | | +----------------------+ + * | | user_data_init | --> | extract_task2_loop_1 | + * v | | +----------------------+ + * +------------------+ | | +----------------------+ + * | single_threaded | | | --> | extract_task2_loop_2 | + * +------------------+ +----------------------+ +----------------------+ + * | +----------------------+ + * +-----> | extract_task2_loop_3 | + * +----------------------+ + */ + const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || + GPU_use_hq_normals_workaround(); + + /* Create an array containing all the extractors that needs to be executed. */ + ExtractorRunDatas extractors; + +#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ + do { \ + if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ + const MeshExtract *extractor = mesh_extract_override_get(&extract_##name, do_hq_normals); \ + extractors.append(extractor); \ + } \ + } while (0) + + EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); + EXTRACT_ADD_REQUESTED(VBO, vbo, uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, tan); + EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); + EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, orco); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); + EXTRACT_ADD_REQUESTED(VBO, vbo, weights); + EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); + EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); + + EXTRACT_ADD_REQUESTED(IBO, ibo, tris); + if (DRW_ibo_requested(mbc->ibo.lines)) { + const MeshExtract *extractor; + if (mbc->ibo.lines_loose != nullptr) { + /* Update #lines_loose ibo. */ + extractor = &extract_lines_with_lines_loose; + } + else { + extractor = &extract_lines; + } + extractors.append(extractor); + } + else if (DRW_ibo_requested(mbc->ibo.lines_loose)) { + /* Note: #ibo.lines must have been created first. */ + const MeshExtract *extractor = &extract_lines_loose_only; + extractors.append(extractor); + } + EXTRACT_ADD_REQUESTED(IBO, ibo, points); + EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); + +#undef EXTRACT_ADD_REQUESTED + + if (extractors.is_empty()) { + return; + } + +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + + eMRIterType iter_type = extractors.iter_types(); + eMRDataType data_flag = extractors.data_types(); + + MeshRenderData *mr = mesh_render_data_create(me, + extraction_cache, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + ts, + iter_type); + mr->use_hide = use_hide; + mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_final_mesh = do_final; + +#ifdef DEBUG_TIME + double rdata_end = PIL_check_seconds_timer(); +#endif + + struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( + task_graph, mr, iter_type, data_flag); + + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + + if (use_thread) { + uint single_threaded_extractors_len = 0; + + /* First run the requested extractors that do not support asynchronous ranges. */ + for (const ExtractorRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (!extractor->use_threading) { + ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); + single_threaded_extractors->append(extractor); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, single_threaded_extractors, mbc, nullptr); + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, + taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + single_threaded_extractors_len++; + } + } + + /* Distribute the remaining extractors into ranges per core. */ + ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas(); + extractors.filter_threaded_extractors_into(*multi_threaded_extractors); + if (!multi_threaded_extractors->is_empty()) { + /* + * Determine the number of thread to use for multithreading. + * Thread can be used for single threaded tasks. These typically take longer to execute so + * fill the rest of the threads for range operations. + */ + int num_threads = BLI_task_scheduler_num_threads(); + num_threads -= single_threaded_extractors_len % num_threads; + + UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData(); + struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( + task_graph, user_data_init_task_data); + + user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( + mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); + + extract_task_in_ranges_create( + task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + } + else { + /* No tasks created freeing extractors list. */ + delete multi_threaded_extractors; + } + } + else { + /* Run all requests on the same thread. */ + ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, extractors_copy, mbc, nullptr); + + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + + /* Trigger the sub-graph for this mesh. */ + BLI_task_graph_node_push_work(task_node_mesh_render_data); + +#ifdef DEBUG_TIME + BLI_task_graph_work_and_wait(task_graph); + double end = PIL_check_seconds_timer(); + + static double avg = 0; + static double avg_fps = 0; + static double avg_rdata = 0; + static double end_prev = 0; + + if (end_prev == 0) { + end_prev = end; + } + + avg = avg * 0.95 + (end - rdata_end) * 0.05; + avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; + avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; + + printf( + "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); + + end_prev = end; +#endif +} + +} // namespace blender::draw + +extern "C" { +void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + blender::draw::mesh_buffer_cache_create_requested(task_graph, + cache, + mbc, + extraction_cache, + me, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + use_subsurf_fdots, + scene, + ts, + use_hide); +} + +} // extern "C" + +/** \} */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index f167ea3d540..0a3945291bb 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -13,7 +13,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * The Original Code is Copyright (C) 2017 by Blender Foundation. + * The Original Code is Copyright (C) 2021 by Blender Foundation. * All rights reserved. */ @@ -25,773 +25,82 @@ #include "MEM_guardedalloc.h" -#include "BLI_alloca.h" -#include "BLI_bitmap.h" -#include "BLI_buffer.h" +#include "atomic_ops.h" + +#include "DNA_object_types.h" + #include "BLI_edgehash.h" #include "BLI_jitter_2d.h" -#include "BLI_math_bits.h" -#include "BLI_math_vector.h" +#include "BLI_kdopbvh.h" #include "BLI_string.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "BKE_bvhutils.h" -#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" #include "BKE_editmesh_cache.h" #include "BKE_editmesh_tangent.h" #include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_mesh_tangent.h" -#include "BKE_modifier.h" -#include "BKE_object_deform.h" #include "BKE_paint.h" -#include "atomic_ops.h" - -#include "bmesh.h" +#include "ED_uvedit.h" -#include "GPU_batch.h" #include "GPU_capabilities.h" -#include "DRW_render.h" - -#include "ED_mesh.h" -#include "ED_uvedit.h" - +#include "draw_cache_extract_mesh_private.h" #include "draw_cache_impl.h" -#include "draw_cache_inline.h" - -#include "draw_cache_extract.h" - -// #define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -/* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). - * \{ */ - -typedef struct MeshRenderData { - eMRExtractType extract_type; - - int poly_len, edge_len, vert_len, loop_len; - int edge_loose_len; - int vert_loose_len; - int loop_loose_len; - int tri_len; - int mat_len; - - bool use_hide; - bool use_subsurf_fdots; - bool use_final_mesh; - - /** Use for #MeshStatVis calculation which use world-space coords. */ - float obmat[4][4]; - - const ToolSettings *toolsettings; - /** Edit Mesh */ - BMEditMesh *edit_bmesh; - BMesh *bm; - EditMeshData *edit_data; - - /* For deformed edit-mesh data. */ - /* Use for #ME_WRAPPER_TYPE_BMESH. */ - const float (*bm_vert_coords)[3]; - const float (*bm_vert_normals)[3]; - const float (*bm_poly_normals)[3]; - const float (*bm_poly_centers)[3]; - - int *v_origindex, *e_origindex, *p_origindex; - int crease_ofs; - int bweight_ofs; - int freestyle_edge_ofs; - int freestyle_face_ofs; - /** Mesh */ - Mesh *me; - const MVert *mvert; - const MEdge *medge; - const MLoop *mloop; - const MPoly *mpoly; - BMVert *eve_act; - BMEdge *eed_act; - BMFace *efa_act; - BMFace *efa_act_uv; - /* Data created on-demand (usually not for #BMesh based data). */ - MLoopTri *mlooptri; - float (*loop_normals)[3]; - float (*poly_normals)[3]; - int *lverts, *ledges; -} MeshRenderData; - -static void mesh_render_data_update_loose_geom(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType UNUSED(data_flag)) -{ - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - mr->ledges[mr->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); - } - - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - mr->lverts[mr->vert_loose_len++] = v; - } - } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } - - MEM_freeN(lvert_map); - - mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); - } - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - mr->lverts[mr->vert_loose_len++] = elem_id; - } - } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } - - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - mr->ledges[mr->edge_loose_len++] = elem_id; - } - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); - } - - mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; - } - } -} - -/** - * Part of the creation of the #MeshRenderData that happens in a thread. - */ -static void mesh_render_data_update_looptris(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); - } - } - else { - /* #BMesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - /* Edit mode ensures this is valid, no need to calculate. */ - BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); - } - } -} - -static void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType UNUSED(iter_type), - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; - const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { - mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); - BKE_mesh_calc_normals_poly((MVert *)mr->mvert, - NULL, - mr->vert_len, - mr->mloop, - mr->mpoly, - mr->loop_len, - mr->poly_len, - mr->poly_normals, - true); - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); - BKE_mesh_normals_loop_split(mr->me->mvert, - mr->vert_len, - mr->me->medge, - mr->edge_len, - mr->me->mloop, - mr->loop_normals, - mr->loop_len, - mr->me->mpoly, - mr->poly_normals, - mr->poly_len, - is_auto_smooth, - split_angle, - NULL, - clnors, - NULL); - } - } - else { - /* #BMesh */ - if (data_flag & MR_DATA_POLY_NOR) { - /* Use #BMFace.no instead. */ - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - - const float(*vert_coords)[3] = NULL; - const float(*vert_normals)[3] = NULL; - const float(*poly_normals)[3] = NULL; - - if (mr->edit_data && mr->edit_data->vertexCos) { - vert_coords = mr->bm_vert_coords; - vert_normals = mr->bm_vert_normals; - poly_normals = mr->bm_poly_normals; - } - - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); - BM_loops_calc_normal_vcos(mr->bm, - vert_coords, - vert_normals, - poly_normals, - is_auto_smooth, - split_angle, - mr->loop_normals, - NULL, - NULL, - clnors_offset, - false); - } - } -} - -/** - * \param is_mode_active: When true, use the modifiers from the edit-data, - * otherwise don't use modifiers as they are not from this object. - */ -static MeshRenderData *mesh_render_data_create(Mesh *me, - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const DRW_MeshCDMask *UNUSED(cd_used), - const ToolSettings *ts, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); - mr->toolsettings = ts; - mr->mat_len = mesh_render_mat_len_get(me); - - copy_m4_m4(mr->obmat, obmat); - - if (is_editmode) { - BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); - mr->bm = me->edit_mesh->bm; - mr->edit_bmesh = me->edit_mesh; - mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; - mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; - - if (mr->edit_data) { - EditMeshData *emd = mr->edit_data; - if (emd->vertexCos) { - BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); - BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); - } - - mr->bm_vert_coords = mr->edit_data->vertexCos; - mr->bm_vert_normals = mr->edit_data->vertexNos; - mr->bm_poly_normals = mr->edit_data->polyNos; - mr->bm_poly_centers = mr->edit_data->polyCos; - } - - bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); - bool use_mapped = is_mode_active && - (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); - - int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; - - BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); - BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); - - mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); - mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); - mr->eed_act = BM_mesh_active_edge_get(mr->bm); - mr->eve_act = BM_mesh_active_vert_get(mr->bm); - - mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); - mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); -#ifdef WITH_FREESTYLE - mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); - mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); -#endif - - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; - /* Seems like the mesh_eval_final do not have the right origin indices. - * Force not mapped in this case. */ - if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { - // mr->edit_bmesh = NULL; - mr->extract_type = MR_EXTRACT_MESH; - } - } - else { - mr->me = me; - mr->edit_bmesh = NULL; - - bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; - } - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - mr->vert_len = mr->me->totvert; - mr->edge_len = mr->me->totedge; - mr->loop_len = mr->me->totloop; - mr->poly_len = mr->me->totpoly; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - - mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); - mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); - mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); - mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); - - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - - mr->vert_len = bm->totvert; - mr->edge_len = bm->totedge; - mr->loop_len = bm->totloop; - mr->poly_len = bm->totface; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - } - mesh_render_data_update_loose_geom(mr, iter_type, data_flag); - - return mr; -} - -static void mesh_render_data_free(MeshRenderData *mr) +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) { - MEM_SAFE_FREE(mr->mlooptri); - MEM_SAFE_FREE(mr->poly_normals); - MEM_SAFE_FREE(mr->loop_normals); - - MEM_SAFE_FREE(mr->lverts); - MEM_SAFE_FREE(mr->ledges); - - MEM_freeN(mr); + /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to + * `MeshBufferCache *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + void *buffer = *buffer_ptr; + BLI_assert(buffer); + return buffer; } -BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +eMRIterType mesh_extract_iter_type(const MeshExtract *ext) { - return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_face_at_index(mr->bm, mr->p_origindex[idx]) : - NULL; -} - -BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) -{ - return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : - NULL; -} - -BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) -{ - return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : - NULL; -} - -BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_coords)[3] = mr->bm_vert_coords; - if (vert_coords != NULL) { - return vert_coords[BM_elem_index_get(eve)]; - } - - UNUSED_VARS(mr); - return eve->co; -} - -BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_normals)[3] = mr->bm_vert_normals; - if (vert_normals != NULL) { - return vert_normals[BM_elem_index_get(eve)]; - } - - UNUSED_VARS(mr); - return eve->no; -} - -BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) -{ - const float(*poly_normals)[3] = mr->bm_poly_normals; - if (poly_normals != NULL) { - return poly_normals[BM_elem_index_get(efa)]; - } - - UNUSED_VARS(mr); - return efa->no; + eMRIterType type = 0; + SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); + SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); + SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); + SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); + return type; } -/** \} */ - /* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loop Triangles +/** \name Override extractors + * Extractors can be overridden. When overridden a specialized version is used. The next functions + * would check for any needed overrides and usage of the specialized version. * \{ */ -typedef struct ExtractTriBMesh_Params { - BMLoop *(*looptris)[3]; - int tri_range[2]; -} ExtractTriBMesh_Params; -typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 3) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } - -typedef struct ExtractTriMesh_Params { - const MLoopTri *mlooptri; - int tri_range[2]; -} ExtractTriMesh_Params; -typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 1) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Polygons, Loops - * \{ */ - -typedef struct ExtractPolyBMesh_Params { - BMLoop *(*looptris)[3]; - int poly_range[2]; -} ExtractPolyBMesh_Params; -typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, - void *data); - -#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_poly = _ftable[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_BM_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_face = _ftable[index_poly]; \ - BMLoop *elem_loop, *l_first; \ - elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ - do { \ - const int index_loop = BM_elem_index_get(elem_loop); \ - (void)index_loop; /* Quiet warning when unused. */ - -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ - } \ - while ((elem_loop = elem_loop->next) != l_first) \ - ; \ - } \ - } - -typedef struct ExtractPolyMesh_Params { - int poly_range[2]; -} ExtractPolyMesh_Params; -typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, - void *data); - -#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_MESH_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ - elem_poly, index_poly, elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const MLoop *_mloop = mr->mloop; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ - for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ - const MLoop *elem_loop = &_mloop[index_loop]; \ - (void)elem_loop; - -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ - } \ - } \ +static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) +{ + if (extractor == &extract_pos_nor) { + return &extract_pos_nor_hq; } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Edges - * \{ */ - -typedef struct ExtractLEdgeBMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeBMesh_Params; -typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, - void *data); - -#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ - CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ - BMEdge **_etable = mr->bm->etable; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLEdgeMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeMesh_Params; -typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, - void *data); - -#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ - CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ - { \ - const MEdge *_medge = mr->medge; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_MESH_END \ - } \ - } \ + if (extractor == &extract_lnor) { + return &extract_lnor_hq; } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Vertices - * \{ */ - -typedef struct ExtractLVertBMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertBMesh_Params; -typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, - void *data); - -#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ - CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMVert **vtable = mr->bm->vtable; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - BMVert *elem_vert = vtable[lverts[index_lvert]]; \ - (void)elem_vert; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLVertMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertMesh_Params; -typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, - void *data); - -#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ - CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ - { \ - const MVert *mvert = mr->mvert; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - const MVert *elem = &mvert[lverts[index_lvert]]; \ - (void)elem; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_MESH_END \ - } \ - } \ + if (extractor == &extract_tan) { + return &extract_tan_hq; } + if (extractor == &extract_fdots_nor) { + return &extract_fdots_nor_hq; + } + return extractor; +} -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ - -typedef void *(ExtractInitFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer); -typedef void(ExtractFinishFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer, - void *data); - -typedef struct MeshExtract { - /** Executed on main thread and return user data for iteration functions. */ - ExtractInitFn *init; - /** Executed on one (or more if use_threading) worker thread(s). */ - ExtractTriBMeshFn *iter_looptri_bm; - ExtractTriMeshFn *iter_looptri_mesh; - ExtractPolyBMeshFn *iter_poly_bm; - ExtractPolyMeshFn *iter_poly_mesh; - ExtractLEdgeBMeshFn *iter_ledge_bm; - ExtractLEdgeMeshFn *iter_ledge_mesh; - ExtractLVertBMeshFn *iter_lvert_bm; - ExtractLVertMeshFn *iter_lvert_mesh; - /** Executed on one worker thread after all elements iterations. */ - ExtractFinishFn *finish; - /** Used to request common data. */ - const eMRDataType data_flag; - /** Used to know if the element callbacks are thread-safe and can be parallelized. */ - const bool use_threading; -} MeshExtract; - -BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals) { - eMRIterType type = 0; - SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); - SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); - SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); - SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); - return type; + if (do_hq_normals) { + extractor = mesh_extract_override_hq_normals(extractor); + } + return extractor; } /** \} */ @@ -855,69 +164,65 @@ static void *extract_tris_init(const MeshRenderData *mr, } static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } + + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(elt[0]->f->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[mat]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(mp->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_tris_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_Tri_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc = &cache->final; + MeshBufferCache *mbc_final = &cache->final; for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc->tris_per_mat[i] == NULL) { - mbc->tris_per_mat[i] = GPU_indexbuf_calloc(); + if (mbc_final->tris_per_mat[i] == NULL) { + mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ const int mat_start = data->tri_mat_start[i]; const int mat_end = data->tri_mat_end[i]; const int start = mat_start * 3; const int len = (mat_end - mat_start) * 3; - GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len); + GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); } } MEM_freeN(data->tri_mat_start); @@ -925,13 +230,14 @@ static void extract_tris_finish(const MeshRenderData *mr, MEM_freeN(data); } -static const MeshExtract extract_tris = { +const MeshExtract extract_tris = { .init = extract_tris_init, .iter_looptri_bm = extract_tris_iter_looptri_bm, .iter_looptri_mesh = extract_tris_iter_looptri_mesh, .finish = extract_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris), }; /** \} */ @@ -951,138 +257,124 @@ static void *extract_lines_init(const MeshRenderData *mr, return elb; } -static void extract_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - /* Using poly & loop iterator would complicate accessing the adjacent loop. */ - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, + BM_elem_index_get(l_iter->e), + BM_elem_index_get(l_iter), + BM_elem_index_get(l_iter->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); + } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; const MEdge *medge = mr->medge; if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } - else { - GPU_indexbuf_set_line_restart(elb, ml->e); - } - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + const MEdge *med = &medge[ml->e]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } + else { + GPU_indexbuf_set_line_restart(elb, ml->e); + } + } while ((ml_index = ml_index_next++) != ml_index_last); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } while ((ml_index = ml_index_next++) != ml_index_last); } } static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - const int l_index_offset = mr->edge_len + ledge_index; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); + const int l_index_offset = mr->edge_len + ledge_index; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); } - EXTRACT_LEDGE_FOREACH_BM_END; + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); } static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int l_index_offset = mr->edge_len + ledge_index; - const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); + const int l_index_offset = mr->edge_len + ledge_index; + const int e_index = mr->ledges[ledge_index]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); } - EXTRACT_LEDGE_FOREACH_MESH_END; + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, e_index); } static void extract_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } -static const MeshExtract extract_lines = { +const MeshExtract extract_lines = { .init = extract_lines_init, .iter_poly_bm = extract_lines_iter_poly_bm, .iter_poly_mesh = extract_lines_iter_poly_mesh, .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines), }; + /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Loose Edges Sub Buffer +/** \name Extract Lines and Loose Edges Sub Buffer * \{ */ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) @@ -1098,24 +390,47 @@ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshB static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); extract_lines_loose_subbuffer(mr, cache); MEM_freeN(elb); } -static const MeshExtract extract_lines_with_lines_loose = { +const MeshExtract extract_lines_with_lines_loose = { .init = extract_lines_init, .iter_poly_bm = extract_lines_iter_poly_bm, .iter_poly_mesh = extract_lines_iter_poly_mesh, .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_with_lines_loose_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loose Edges Sub Buffer + * \{ */ + +static void *extract_lines_loose_only_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf) +{ + BLI_assert(buf == cache->final.ibo.lines_loose); + UNUSED_VARS_NDEBUG(buf); + extract_lines_loose_subbuffer(mr, cache); + return NULL; +} + +const MeshExtract extract_lines_loose_only = { + .init = extract_lines_loose_only_init, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose)}; /** \} */ @@ -1159,86 +474,81 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, } } -static void extract_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - vert_set_bm(elb, l->v, l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + vert_set_bm(elb, l_iter->v, l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; vert_set_mesh(elb, mr, ml->v, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); - vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_BM_END; + vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); + vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); - vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_MESH_END; + vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); + vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - vert_set_bm(elb, eve, offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_BM_END; + vert_set_bm(elb, eve, offset + lvert_index); } static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_MESH_END; + + vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); } static void extract_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } -static const MeshExtract extract_points = { +const MeshExtract extract_points = { .init = extract_points_init, .iter_poly_bm = extract_points_iter_poly_bm, .iter_poly_mesh = extract_points_iter_poly_mesh, @@ -1247,14 +557,15 @@ static const MeshExtract extract_points = { .iter_lvert_bm = extract_points_iter_lvert_bm, .iter_lvert_mesh = extract_points_iter_lvert_mesh, .finish = extract_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points), }; /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Indices +/** \name Extract Face-dots Indices * \{ */ static void *extract_fdots_init(const MeshRenderData *mr, @@ -1266,70 +577,66 @@ static void *extract_fdots_init(const MeshRenderData *mr, return elb; } -static void extract_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *elb) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); } - EXTRACT_POLY_FOREACH_BM_END; } static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *elb) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; const MVert *mv = &mr->mvert[ml->v]; if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); + return; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; + GPU_indexbuf_set_point_restart(elb, mp_index); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); } - EXTRACT_POLY_FOREACH_MESH_END; } } static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } -static const MeshExtract extract_fdots = { +const MeshExtract extract_fdots = { .init = extract_fdots_init, .iter_poly_bm = extract_fdots_iter_poly_bm, .iter_poly_mesh = extract_fdots_iter_poly_mesh, .finish = extract_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots), }; /** \} */ @@ -1346,7 +653,7 @@ typedef struct MeshExtract_LinePaintMask_Data { static void *extract_lines_paint_mask_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) + void *UNUSED(ibo)) { size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); @@ -1355,12 +662,16 @@ static void *extract_lines_paint_mask_init(const MeshRenderData *mr, } static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_LinePaintMask_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int e_index = ml->e; const MEdge *me = &mr->medge[e_index]; if (!((mr->use_hide && (me->flag & ME_HIDE)) || @@ -1390,26 +701,26 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, GPU_indexbuf_set_line_restart(&data->elb, e_index); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } + static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); } -static const MeshExtract extract_lines_paint_mask = { +const MeshExtract extract_lines_paint_mask = { .init = extract_lines_paint_mask_init, .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, .finish = extract_lines_paint_mask_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; /** \} */ @@ -1487,49 +798,44 @@ BLI_INLINE void lines_adjacency_triangle( } static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LineAdjacency_Data *data = _data; /* Create edges for remaining non manifold edges. */ EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); @@ -1559,14 +865,14 @@ static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), #undef NO_EDGE -static const MeshExtract extract_lines_adjacency = { +const MeshExtract extract_lines_adjacency = { .init = extract_lines_adjacency_init, .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, .finish = extract_lines_adjacency_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; /** \} */ @@ -1598,56 +904,51 @@ BLI_INLINE void edituv_tri_add( } static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); } static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); } -static const MeshExtract extract_edituv_tris = { +const MeshExtract extract_edituv_tris = { .init = extract_edituv_tris_init, .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, .finish = extract_edituv_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; /** \} */ @@ -1674,27 +975,34 @@ BLI_INLINE void edituv_edge_add( } } -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + edituv_edge_add(data, - BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), l_index, - BM_elem_index_get(loop->next)); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int ml_index_last = mp->totloop + mp->loopstart - 1; const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); @@ -1704,27 +1012,27 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, ml_index, ml_index_next); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); } -static const MeshExtract extract_edituv_lines = { +const MeshExtract extract_edituv_lines = { .init = extract_edituv_lines_init, .iter_poly_bm = extract_edituv_lines_iter_poly_bm, .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, .finish = extract_edituv_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; /** \} */ @@ -1753,57 +1061,62 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - edituv_point_add(data, - BM_elem_flag_test(l->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(l->f, BM_ELEM_SELECT), - l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && mr->v_origindex[ml->v] != ORIGINDEX_NONE); edituv_point_add( data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); } -static const MeshExtract extract_edituv_points = { +const MeshExtract extract_edituv_points = { .init = extract_edituv_points_init, .iter_poly_bm = extract_edituv_points_iter_poly_bm, .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, .finish = extract_edituv_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Facedots Indices +/** \name Extract Edit UV Face-dots Indices * \{ */ static void *extract_edituv_fdots_init(const MeshRenderData *mr, @@ -1830,26 +1143,29 @@ BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - edituv_facedot_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index); - } - EXTRACT_POLY_FOREACH_BM_END; + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); } static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = (!mr->use_subsurf_fdots || @@ -1859,40 +1175,34 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, (mp->flag & ME_FACE_SEL) != 0, mp_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - EXTRACT_POLY_FOREACH_MESH_END; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); } -static const MeshExtract extract_edituv_fdots = { +const MeshExtract extract_edituv_fdots = { .init = extract_edituv_fdots_init, .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, .finish = extract_edituv_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; /** \} */ @@ -1914,6 +1224,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorLoop struct accordingly. */ @@ -1921,7 +1232,6 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -1949,28 +1259,34 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, } static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - vert->nor = data->normals[BM_elem_index_get(l->v)].low; - BMFace *efa = l->f; - vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; + vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -1988,85 +1304,75 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, vert->nor.w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; - vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; - } - EXTRACT_LEDGE_FOREACH_BM_END; + + int l_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; + vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; } static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - vert[0].nor = data->normals[med->v1].low; - vert[1].nor = data->normals[med->v2].low; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + vert[0].nor = data->normals[med->v1].low; + vert[1].nor = data->normals[med->v2].low; } static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - vert->nor = data->normals[BM_elem_index_get(eve)].low; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + vert->nor = data->normals[BM_elem_index_get(eve)].low; } static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[v_index].low; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[v_index].low; } static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); } -static const MeshExtract extract_pos_nor = { +const MeshExtract extract_pos_nor = { .init = extract_pos_nor_init, .iter_poly_bm = extract_pos_nor_iter_poly_bm, .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, @@ -2075,9 +1381,11 @@ static const MeshExtract extract_pos_nor = { .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, .finish = extract_pos_nor_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor), }; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -2098,6 +1406,7 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorHQLoop struct accordingly. */ @@ -2105,7 +1414,6 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -2133,29 +1441,35 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, } static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); - BMFace *efa = l->f; + BMFace *efa = l_iter->f; vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -2174,91 +1488,80 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, vert->nor[3] = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_BM_END; + int l_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[v_index].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[v_index].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); } -static const MeshExtract extract_pos_nor_hq = { +const MeshExtract extract_pos_nor_hq = { .init = extract_pos_nor_hq_init, .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm, .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh, @@ -2267,9 +1570,9 @@ static const MeshExtract extract_pos_nor_hq = { .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, .finish = extract_pos_nor_hq_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2284,12 +1587,12 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2297,36 +1600,37 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, } static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); } else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f)); + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); } } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; if (mr->loop_normals) { normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); @@ -2352,15 +1656,15 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static const MeshExtract extract_lnor_hq = { +const MeshExtract extract_lnor_hq = { .init = extract_lnor_hq_init, .iter_poly_bm = extract_lnor_hq_iter_poly_bm, .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, + .data_type = MR_DATA_LOOP_NOR, .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor), }; /** \} */ @@ -2372,12 +1676,12 @@ static void *extract_lnor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2385,40 +1689,39 @@ static void *extract_lnor_init(const MeshRenderData *mr, } static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + bm_vert_no_get(mr, l_iter->v)); } else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f)); + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); } - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; if (mr->loop_normals) { *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); @@ -2444,15 +1747,15 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static const MeshExtract extract_lnor = { +const MeshExtract extract_lnor = { .init = extract_lnor_init, .iter_poly_bm = extract_lnor_iter_poly_bm, .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, + .data_type = MR_DATA_LOOP_NOR, .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor), }; /** \} */ @@ -2463,6 +1766,7 @@ static const MeshExtract extract_lnor = { static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2512,7 +1816,6 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca v_len = 1; } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -2545,10 +1848,11 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca return NULL; } -static const MeshExtract extract_uv = { +const MeshExtract extract_uv = { .init = extract_uv_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv), }; /** \} */ @@ -2557,10 +1861,10 @@ static const MeshExtract extract_uv = { /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) +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; @@ -2731,14 +2035,15 @@ static void extract_tan_ex(const MeshRenderData *mr, static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, false); + extract_tan_ex_init(mr, cache, buf, false); return NULL; } -static const MeshExtract extract_tan = { +const MeshExtract extract_tan = { .init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan), }; /** \} */ @@ -2749,13 +2054,13 @@ static const MeshExtract extract_tan = { static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, true); + extract_tan_ex_init(mr, cache, buf, true); return NULL; } -static const MeshExtract extract_tan_hq = { +const MeshExtract extract_tan_hq = { .init = extract_tan_hq_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, }; @@ -2769,6 +2074,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; @@ -2783,7 +2089,6 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2852,12 +2157,12 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, return NULL; } -static const MeshExtract extract_sculpt_data = { +const MeshExtract extract_sculpt_data = { .init = extract_sculpt_data_init, - .data_flag = 0, + .data_type = 0, /* TODO: enable threading. */ .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; /** \} */ @@ -2867,6 +2172,7 @@ static const MeshExtract extract_sculpt_data = { static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2928,7 +2234,6 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3001,10 +2306,11 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * return NULL; } -static const MeshExtract extract_vcol = { +const MeshExtract extract_vcol = { .init = extract_vcol_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol), }; /** \} */ @@ -3022,6 +2328,7 @@ static void *extract_orco_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex @@ -3031,7 +2338,6 @@ static void *extract_orco_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3045,32 +2351,36 @@ static void *extract_orco_init(const MeshRenderData *mr, return data; } -static void extract_orco_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); float *loop_orco = orco_data->vbo_data[l_index]; - copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]); + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + } while ((l_iter = l_iter->next) != l_first); } static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; float *loop_orco = orco_data->vbo_data[ml_index]; copy_v3_v3(loop_orco, orco_data->orco[ml->v]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_orco_finish(const MeshRenderData *UNUSED(mr), @@ -3081,13 +2391,14 @@ static void extract_orco_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_orco = { +const MeshExtract extract_orco = { .init = extract_orco_init, .iter_poly_bm = extract_orco_iter_poly_bm, .iter_poly_mesh = extract_orco_iter_poly_mesh, .finish = extract_orco_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco), }; /** \} */ @@ -3124,11 +2435,12 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3159,43 +2471,47 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, } static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_edge_is_manifold(l->e)) { - float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f), - bm_vert_co_get(mr, l->v), - bm_vert_no_get(mr, l->v), - bm_vert_co_get(mr, l->next->v)); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + if (BM_edge_is_manifold(l_iter->e)) { + float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), + bm_vert_co_get(mr, l_iter->v), + bm_vert_no_get(mr, l_iter->v), + bm_vert_co_get(mr, l_iter->next->v)); data->vbo_data[l_index] = ratio * 253 + 1; } else { data->vbo_data[l_index] = 255; } - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; - if (data->use_edge_render) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + if (data->use_edge_render) { const MEdge *med = &mr->medge[ml->e]; data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + /* Count loop per edge to detect non-manifold. */ if (data->edge_loop_count[ml->e] < 3) { data->edge_loop_count[ml->e]++; @@ -3217,34 +2533,28 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, data->vbo_data[ml_index] = 255; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *UNUSED(eed), + const int ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_BM_END; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; } static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; - data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + + data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; + data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; } static void extract_edge_fac_finish(const MeshRenderData *mr, @@ -3252,10 +2562,10 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) { + GPUVertBuf *vbo = buf; MeshExtract_EdgeFac_Data *data = _data; if (GPU_crappy_amd_driver()) { - GPUVertBuf *vbo = (GPUVertBuf *)buf; /* Some AMD drivers strangely crash with VBO's with a one byte format. * To workaround we reinitialize the VBO with another format and convert * all bytes to floats. */ @@ -3281,16 +2591,16 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, MEM_freeN(data); } -static const MeshExtract extract_edge_fac = { +const MeshExtract extract_edge_fac = { .init = extract_edge_fac_init, .iter_poly_bm = extract_edge_fac_iter_poly_bm, .iter_poly_mesh = extract_edge_fac_iter_poly_mesh, .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, .finish = extract_edge_fac_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -3361,11 +2671,11 @@ static void *extract_weights_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3389,48 +2699,44 @@ static void *extract_weights_init(const MeshRenderData *mr, return data; } -static void extract_weights_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->cd_ofs != -1) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (data->cd_ofs != -1) { + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + else { data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->dvert != NULL) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (data->dvert != NULL) { const MDeformVert *dvert = &data->dvert[ml->v]; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - const MDeformVert *dvert = NULL; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + const MDeformVert *dvert = NULL; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -3442,13 +2748,14 @@ static void extract_weights_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_weights = { +const MeshExtract extract_weights = { .init = extract_weights_init, .iter_poly_bm = extract_weights_iter_poly_bm, .iter_poly_mesh = extract_weights_iter_poly_mesh, .finish = extract_weights_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights), }; /** \} */ @@ -3599,40 +2906,45 @@ static void *extract_edit_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); } static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { EditLoopData *data = (EditLoopData *)_data + l_index; memset(data, 0x0, sizeof(*data)); - mesh_render_data_face_flag(mr, l->f, -1, data); - mesh_render_data_edge_flag(mr, l->e, data); - mesh_render_data_vert_flag(mr, l->v, data); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_face_flag(mr, f, -1, data); + mesh_render_data_edge_flag(mr, l_iter->e, data); + mesh_render_data_vert_flag(mr, l_iter->v, data); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; EditLoopData *data = (EditLoopData *)_data + ml_index; memset(data, 0x0, sizeof(*data)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3648,84 +2960,72 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, mesh_render_data_vert_flag(mr, eve, data); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); - memset(data, 0x0, sizeof(*data) * 2); - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - mesh_render_data_vert_flag(mr, eed->v1, &data[0]); - mesh_render_data_vert_flag(mr, eed->v2, &data[1]); - } - EXTRACT_LEDGE_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); } static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; - memset(data, 0x0, sizeof(*data) * 2); - const int e_index = mr->ledges[ledge_index]; - BMEdge *eed = bm_original_edge_get(mr, e_index); - BMVert *eve1 = bm_original_vert_get(mr, med->v1); - BMVert *eve2 = bm_original_vert_get(mr, med->v2); - if (eed) { - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - } - if (eve1) { - mesh_render_data_vert_flag(mr, eve1, &data[0]); - } - if (eve2) { - mesh_render_data_vert_flag(mr, eve2, &data[1]); - } + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + memset(data, 0x0, sizeof(*data) * 2); + const int e_index = mr->ledges[ledge_index]; + BMEdge *eed = bm_original_edge_get(mr, e_index); + BMVert *eve1 = bm_original_vert_get(mr, med->v1); + BMVert *eve2 = bm_original_vert_get(mr, med->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); } - EXTRACT_LEDGE_FOREACH_MESH_END; } static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_vert_flag(mr, eve, data); - } - EXTRACT_LVERT_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); } static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - const int v_index = mr->lverts[lvert_index]; - BMVert *eve = bm_original_vert_get(mr, v_index); - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } + + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + const int v_index = mr->lverts[lvert_index]; + BMVert *eve = bm_original_vert_get(mr, v_index); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); } - EXTRACT_LVERT_FOREACH_MESH_END; } -static const MeshExtract extract_edit_data = { +const MeshExtract extract_edit_data = { .init = extract_edit_data_init, .iter_poly_bm = extract_edit_data_iter_poly_bm, .iter_poly_mesh = extract_edit_data_iter_poly_mesh, @@ -3733,9 +3033,9 @@ static const MeshExtract extract_edit_data = { .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, .iter_lvert_bm = extract_edit_data_iter_lvert_bm, .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; /** \} */ @@ -3752,6 +3052,7 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ @@ -3759,7 +3060,6 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3772,28 +3072,34 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, } static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); MeshExtract_EditUVData_Data *data = _data; EditLoopData *eldata = &data->vbo_data[l_index]; memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); - mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EditUVData_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + EditLoopData *eldata = &data->vbo_data[ml_index]; memset(eldata, 0x0, sizeof(*eldata)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3823,7 +3129,6 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -3834,14 +3139,14 @@ static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_edituv_data = { +const MeshExtract extract_edituv_data = { .init = extract_edituv_data_init, .iter_poly_bm = extract_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, .finish = extract_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; /** \} */ @@ -3853,12 +3158,12 @@ static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3880,11 +3185,12 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t return (ratio > 1.0f) ? (1.0f / ratio) : ratio; } -static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(data)) +static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; float tot_area = 0.0f, tot_uv_area = 0.0f; float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); @@ -3926,7 +3232,6 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, } /* Copy face data for each loop. */ - GPUVertBuf *vbo = buf; uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -3952,12 +3257,12 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, MEM_freeN(area_ratio); } -static const MeshExtract extract_edituv_stretch_area = { +const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, - .finish = mesh_edituv_stretch_area_finish, - .data_flag = 0, + .finish = extract_edituv_stretch_area_finish, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; /** \} */ @@ -4023,6 +3328,7 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* Waning: adjust #UVStretchAngle struct accordingly. */ @@ -4030,7 +3336,6 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4049,21 +3354,24 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, } static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + const MLoopUV *luv, *luv_next; - BMLoop *l_next = l->next; - BMFace *efa = l->f; - if (l == BM_FACE_FIRST_LOOP(efa)) { + BMLoop *l_next = l_iter->next; + if (l_iter == BM_FACE_FIRST_LOOP(f)) { /* First loop in face. */ - BMLoop *l_tmp = l->prev; - BMLoop *l_next_tmp = l; + BMLoop *l_tmp = l_iter->prev; + BMLoop *l_next_tmp = l_iter; luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); compute_normalize_edge_vectors(auv, @@ -4076,7 +3384,7 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v2_v2(last_auv, auv[1]); copy_v3_v3(last_av, av[1]); } - if (l_next == BM_FACE_FIRST_LOOP(efa)) { + if (l_next == BM_FACE_FIRST_LOOP(f)) { /* Move previous edge. */ copy_v2_v2(auv[0], auv[1]); copy_v3_v3(av[0], av[1]); @@ -4085,27 +3393,31 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v3_v3(av[1], last_av); } else { - luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); + luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); - compute_normalize_edge_vectors( - auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v)); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_iter->v), + bm_vert_co_get(mr, l_next->v)); } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop; + int l_next = ml_index + 1; const MVert *v, *v_next; if (ml_index == mp->loopstart) { /* First loop in face. */ @@ -4136,7 +3448,6 @@ static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), @@ -4147,14 +3458,14 @@ static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr) MEM_freeN(data); } -static const MeshExtract extract_edituv_stretch_angle = { +const MeshExtract extract_edituv_stretch_angle = { .init = extract_edituv_stretch_angle_init, .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, .finish = extract_edituv_stretch_angle_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; /** \} */ @@ -4166,12 +3477,12 @@ static void *extract_mesh_analysis_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4725,14 +4036,14 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) MEM_freeN(vert_angles); } -static void extract_mesh_analysis_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) +static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; BLI_assert(mr->edit_bmesh); - GPUVertBuf *vbo = buf; float *l_weight = (float *)GPU_vertbuf_get_data(vbo); switch (mr->toolsettings->statvis.type) { @@ -4754,103 +4065,99 @@ static void extract_mesh_analysis_finish(const MeshRenderData *mr, } } -static const MeshExtract extract_mesh_analysis = { +const MeshExtract extract_mesh_analysis = { .init = extract_mesh_analysis_init, - .finish = extract_mesh_analysis_finish, + .finish = extract_analysis_iter_finish_mesh, /* This is not needed for all visualization types. * * Maybe split into different extract. */ - .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots positions +/** \name Extract Face-dots positions * \{ */ static void *extract_fdots_pos_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int f_index, void *data) { float(*center)[3] = data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - float *co = center[f_index]; - zero_v3(co); + float *co = center[f_index]; + zero_v3(co); - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); - } while ((l_iter = l_iter->next) != l_first); - mul_v3_fl(co, 1.0f / (float)f->len); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); } static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { float(*center)[3] = (float(*)[3])data; + float *co = center[mp_index]; + zero_v3(co); + const MVert *mvert = mr->mvert; const MLoop *mloop = mr->mloop; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v3_v3(center[mp_index], mv->co); + break; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - float *co = center[mp_index]; - zero_v3(co); - - const MLoop *ml = &mloop[mp->loopstart]; - for (int i = 0; i < mp->totloop; i++, ml++) { - const MVert *mv = &mvert[ml->v]; - add_v3_v3(center[mp_index], mv->co); - } - mul_v3_fl(co, 1.0f / (float)mp->totloop); + else { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); } - EXTRACT_POLY_FOREACH_MESH_END; + } + + if (!mr->use_subsurf_fdots) { + mul_v3_fl(co, 1.0f / (float)mp->totloop); } } -static const MeshExtract extract_fdots_pos = { +const MeshExtract extract_fdots_pos = { .init = extract_fdots_pos_init, .iter_poly_bm = extract_fdots_pos_iter_poly_bm, .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Normal and edit flag +/** \name Extract Face-dots Normal and edit flag * \{ */ #define NOR_AND_FLAG_DEFAULT 0 #define NOR_AND_FLAG_SELECT 1 @@ -4861,11 +4168,12 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4877,8 +4185,8 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -4921,27 +4229,28 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, } } -static const MeshExtract extract_fdots_nor = { +const MeshExtract extract_fdots_nor = { .init = extract_fdots_nor_init, .finish = extract_fdots_nor_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots High Quality Normal and edit flag +/** \name Extract Face-dots High Quality Normal and edit flag * \{ */ static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4953,8 +4262,8 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; short *nor = (short *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -4997,17 +4306,17 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, } } -static const MeshExtract extract_fdots_nor_hq = { +const MeshExtract extract_fdots_nor_hq = { .init = extract_fdots_nor_hq_init, .finish = extract_fdots_nor_hq_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots UV +/** \name Extract Face-dots UV * \{ */ typedef struct MeshExtract_FdotUV_Data { @@ -5020,13 +4329,14 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "au"); GPU_vertformat_alias_add(&format, "pos"); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5047,42 +4357,41 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, return data; } -static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_FdotUV_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - float w = 1.0f / (float)l->f->len; - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); - madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float w = 1.0f / (float)f->len; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); + } while ((l_iter = l_iter->next) != l_first); } static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_FdotUV_Data *data = _data; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { float w = 1.0f / (float)mp->totloop; madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -5094,18 +4403,19 @@ static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_fdots_uv = { +const MeshExtract extract_fdots_uv = { .init = extract_fdots_uv_init, .iter_poly_bm = extract_fdots_uv_iter_poly_bm, .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, .finish = extract_fdots_uv_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; + /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Edit UV flag +/** \name Extract Face-dots Edit UV flag * \{ */ typedef struct MeshExtract_EditUVFdotData_Data { @@ -5117,11 +4427,12 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5132,34 +4443,28 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, } static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); - } - EXTRACT_POLY_FOREACH_BM_END; + EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); } static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[mp_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); - } + EditLoopData *eldata = &data->vbo_data[mp_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); } - EXTRACT_POLY_FOREACH_MESH_END; } static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -5170,14 +4475,15 @@ static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -static const MeshExtract extract_fdots_edituv_data = { +const MeshExtract extract_fdots_edituv_data = { .init = extract_fdots_edituv_data_init, .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, .finish = extract_fdots_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5193,6 +4499,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; /* Exclusively for edit mode. */ BLI_assert(mr->bm); @@ -5201,7 +4508,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); @@ -5228,11 +4535,11 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, return NULL; } -static const MeshExtract extract_skin_roots = { +const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; /** \} */ @@ -5244,12 +4551,12 @@ static void *extract_select_idx_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); @@ -5260,169 +4567,163 @@ static void *extract_select_idx_init(const MeshRenderData *mr, * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the * shader to output original index. */ -static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = f_index; + } while ((l_iter = l_iter->next) != l_first); } -static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); } -static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); } static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); } static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); - } - EXTRACT_LVERT_FOREACH_BM_END; + + ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); } static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int e_index = mr->ledges[ledge_index]; - const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int e_index = mr->ledges[ledge_index]; + const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; } static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; - int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; } static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr) - { - const int v_index = mr->lverts[lvert_index]; - const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int v_index = mr->lverts[lvert_index]; + const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; + ((uint32_t *)data)[offset + lvert_index] = v_orig; } -static const MeshExtract extract_poly_idx = { +const MeshExtract extract_poly_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_poly_idx_iter_poly_bm, .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; -static const MeshExtract extract_edge_idx = { +const MeshExtract extract_edge_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_edge_idx_iter_poly_bm, .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; -static const MeshExtract extract_vert_idx = { +const MeshExtract extract_vert_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_vert_idx_iter_poly_bm, .iter_poly_mesh = extract_vert_idx_iter_poly_mesh, @@ -5430,736 +4731,51 @@ static const MeshExtract extract_vert_idx = { .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; -static void *extract_select_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void *extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } -static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *UNUSED(f), + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - ((uint32_t *)data)[f_index] = f_index; - } - EXTRACT_POLY_FOREACH_BM_END; + ((uint32_t *)data)[f_index] = f_index; } static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *data) { if (mr->p_origindex != NULL) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mp_index; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mp_index; } } -static const MeshExtract extract_fdot_idx = { - .init = extract_select_fdot_idx_init, +const MeshExtract extract_fdot_idx = { + .init = extract_fdot_idx_init, .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name ExtractTaskData - * \{ */ -typedef struct ExtractUserData { - void *user_data; -} ExtractUserData; - -typedef enum ExtractTaskDataType { - EXTRACT_MESH_EXTRACT, - EXTRACT_LINES_LOOSE, -} ExtractTaskDataType; - -typedef struct ExtractTaskData { - void *next, *prev; - const MeshRenderData *mr; - struct MeshBatchCache *cache; - const MeshExtract *extract; - ExtractTaskDataType tasktype; - eMRIterType iter_type; - int start, end; - /** Decremented each time a task is finished. */ - int32_t *task_counter; - void *buf; - ExtractUserData *user_data; -} ExtractTaskData; - -static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) -{ - ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__); - taskdata->next = NULL; - taskdata->prev = NULL; - taskdata->tasktype = EXTRACT_MESH_EXTRACT; - taskdata->mr = mr; - taskdata->cache = cache; - taskdata->extract = extract; - taskdata->buf = buf; - - /* #ExtractUserData is shared between the iterations as it holds counters to detect if the - * extraction is finished. To make sure the duplication of the user_data does not create a new - * instance of the counters we allocate the user_data in its own container. - * - * This structure makes sure that when extract_init is called, that the user data of all - * iterations are updated. */ - taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__); - taskdata->iter_type = mesh_extract_iter_type(extract); - taskdata->task_counter = task_counter; - taskdata->start = 0; - taskdata->end = INT_MAX; - return taskdata; -} - -static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr, - struct MeshBatchCache *cache) -{ - ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__); - taskdata->tasktype = EXTRACT_LINES_LOOSE; - taskdata->mr = mr; - taskdata->cache = cache; - return taskdata; -} - -static void extract_task_data_free(void *data) -{ - ExtractTaskData *task_data = data; - MEM_SAFE_FREE(task_data->user_data); - MEM_freeN(task_data); -} - -BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, - const eMRIterType iter_type, - int start, - int end, - const MeshExtract *extract, - void *user_data) -{ - switch (mr->extract_type) { - case MR_EXTRACT_BMESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_POLY) { - extract->iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); - } - break; - case MR_EXTRACT_MAPPED: - case MR_EXTRACT_MESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_POLY) { - extract->iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); - } - if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); - } - break; - } -} - -static void extract_init(ExtractTaskData *data) -{ - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf); - } -} - -static void extract_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - mesh_extract_iter(data->mr, - data->iter_type, - data->start, - data->end, - data->extract, - data->user_data->user_data); - - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0 && data->extract->finish != NULL) { - data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data); - } - } - else if (data->tasktype == EXTRACT_LINES_LOOSE) { - extract_lines_loose_subbuffer(data->mr, data->cache); - } -} - -static void extract_init_and_run(void *__restrict taskdata) -{ - extract_init((ExtractTaskData *)taskdata); - extract_run(taskdata); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Update Mesh Render Data - * \{ */ -typedef struct MeshRenderDataUpdateTaskData { - MeshRenderData *mr; - eMRIterType iter_type; - eMRDataType data_flag; -} MeshRenderDataUpdateTaskData; - -static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) -{ - BLI_assert(taskdata); - MeshRenderData *mr = taskdata->mr; - mesh_render_data_free(mr); - MEM_freeN(taskdata); -} - -static void mesh_extract_render_data_node_exec(void *__restrict task_data) -{ - MeshRenderDataUpdateTaskData *update_task_data = task_data; - MeshRenderData *mr = update_task_data->mr; - const eMRIterType iter_type = update_task_data->iter_type; - const eMRDataType data_flag = update_task_data->data_flag; - - mesh_render_data_update_normals(mr, iter_type, data_flag); - mesh_render_data_update_looptris(mr, iter_type, data_flag); -} - -static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, - MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData), - __func__); - task_data->mr = mr; - task_data->iter_type = iter_type; - task_data->data_flag = data_flag; - - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - mesh_extract_render_data_node_exec, - task_data, - (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Extract Single Threaded - * \{ */ -typedef struct ExtractSingleThreadedTaskData { - ListBase task_datas; -} ExtractSingleThreadedTaskData; - -static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata) -{ - BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_freeN(taskdata); -} - -static void extract_single_threaded_task_node_exec(void *__restrict task_data) -{ - ExtractSingleThreadedTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init_and_run(td); - } -} - -static struct TaskNode *extract_single_threaded_task_node_create( - struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - extract_single_threaded_task_node_exec, - task_data, - (TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - UserData Initializer - * \{ */ -typedef struct UserDataInitTaskData { - ListBase task_datas; - int32_t *task_counters; - -} UserDataInitTaskData; - -static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) -{ - BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_SAFE_FREE(taskdata->task_counters); - MEM_freeN(taskdata); -} - -static void user_data_init_task_data_exec(void *__restrict task_data) -{ - UserDataInitTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init(td); - } -} - -static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, - UserDataInitTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - user_data_init_task_data_exec, - task_data, - (TaskGraphNodeFreeFunction)user_data_init_task_data_free); - return task_node; -} - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Loop - * \{ */ - -static void extract_range_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata, - const eMRIterType type, - int start, - int length) -{ - taskdata = MEM_dupallocN(taskdata); - atomic_add_and_fetch_int32(taskdata->task_counter, 1); - taskdata->iter_type = type; - taskdata->start = start; - taskdata->end = start + length; - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_run, taskdata, MEM_freeN); - BLI_task_graph_edge_create(task_node_user_data_init, task_node); -} - -static void extract_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_mesh_render_data, - struct TaskNode *task_node_user_data_init, - ListBase *single_threaded_task_datas, - ListBase *user_data_init_task_datas, - const Scene *scene, - const MeshRenderData *mr, - MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) -{ - BLI_assert(scene != NULL); - const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || - GPU_use_hq_normals_workaround(); - if (do_hq_normals) { - if (extract == &extract_lnor) { - extract = &extract_lnor_hq; - } - else if (extract == &extract_pos_nor) { - extract = &extract_pos_nor_hq; - } - else if (extract == &extract_tan) { - extract = &extract_tan_hq; - } - else if (extract == &extract_fdots_nor) { - extract = &extract_fdots_nor_hq; - } - } - - /* Divide extraction of the VBO/IBO into sensible chunks of works. */ - ExtractTaskData *taskdata = extract_task_data_create_mesh_extract( - mr, cache, extract, buf, task_counter); - - /* Simple heuristic. */ - const int chunk_size = 8192; - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size; - if (use_thread && extract->use_threading) { - - /* Divide task into sensible chunks. */ - if (taskdata->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size); - } - } - BLI_addtail(user_data_init_task_datas, taskdata); - } - else if (use_thread) { - /* One task for the whole VBO. */ - (*task_counter)++; - struct TaskNode *one_task = BLI_task_graph_node_create( - task_graph, extract_init_and_run, taskdata, extract_task_data_free); - BLI_task_graph_edge_create(task_node_mesh_render_data, one_task); - } - else { - /* Single threaded extraction. */ - (*task_counter)++; - BLI_addtail(single_threaded_task_datas, taskdata); - } -} - -void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, - MeshBatchCache *cache, - MeshBufferCache mbc, - Mesh *me, - - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, - const Scene *scene, - const ToolSettings *ts, - const bool use_hide) -{ - /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. - * This sub-graph starts with an extract_render_data_node. This fills/converts the required data - * from Mesh. - * - * Small extractions and extractions that can't be multi-threaded are grouped in a single - * `extract_single_threaded_task_node`. - * - * Other extractions will create a node for each loop exceeding 8192 items. these nodes are - * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the - * user_data needed for the extraction based on the data extracted from the mesh. - * counters are used to check if the finalize of a task has to be called. - * - * Mesh extraction sub graph - * - * +----------------------+ - * +-----> | extract_task1_loop_1 | - * | +----------------------+ - * +------------------+ +----------------------+ +----------------------+ - * | mesh_render_data | --> | | --> | extract_task1_loop_2 | - * +------------------+ | | +----------------------+ - * | | | +----------------------+ - * | | user_data_init | --> | extract_task2_loop_1 | - * v | | +----------------------+ - * +------------------+ | | +----------------------+ - * | single_threaded | | | --> | extract_task2_loop_2 | - * +------------------+ +----------------------+ +----------------------+ - * | +----------------------+ - * +-----> | extract_task2_loop_3 | - * +----------------------+ - */ - eMRIterType iter_flag = 0; - eMRDataType data_flag = 0; - - const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL; - -#define TEST_ASSIGN(type, type_lowercase, name) \ - do { \ - if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ - iter_flag |= mesh_extract_iter_type(&extract_##name); \ - data_flag |= extract_##name.data_flag; \ - } \ - } while (0) - - TEST_ASSIGN(VBO, vbo, pos_nor); - TEST_ASSIGN(VBO, vbo, lnor); - TEST_ASSIGN(VBO, vbo, uv); - TEST_ASSIGN(VBO, vbo, tan); - TEST_ASSIGN(VBO, vbo, vcol); - TEST_ASSIGN(VBO, vbo, sculpt_data); - TEST_ASSIGN(VBO, vbo, orco); - TEST_ASSIGN(VBO, vbo, edge_fac); - TEST_ASSIGN(VBO, vbo, weights); - TEST_ASSIGN(VBO, vbo, edit_data); - TEST_ASSIGN(VBO, vbo, edituv_data); - TEST_ASSIGN(VBO, vbo, edituv_stretch_area); - TEST_ASSIGN(VBO, vbo, edituv_stretch_angle); - TEST_ASSIGN(VBO, vbo, mesh_analysis); - TEST_ASSIGN(VBO, vbo, fdots_pos); - TEST_ASSIGN(VBO, vbo, fdots_nor); - TEST_ASSIGN(VBO, vbo, fdots_uv); - TEST_ASSIGN(VBO, vbo, fdots_edituv_data); - TEST_ASSIGN(VBO, vbo, poly_idx); - TEST_ASSIGN(VBO, vbo, edge_idx); - TEST_ASSIGN(VBO, vbo, vert_idx); - TEST_ASSIGN(VBO, vbo, fdot_idx); - TEST_ASSIGN(VBO, vbo, skin_roots); - - TEST_ASSIGN(IBO, ibo, tris); - TEST_ASSIGN(IBO, ibo, lines); - TEST_ASSIGN(IBO, ibo, points); - TEST_ASSIGN(IBO, ibo, fdots); - TEST_ASSIGN(IBO, ibo, lines_paint_mask); - TEST_ASSIGN(IBO, ibo, lines_adjacency); - TEST_ASSIGN(IBO, ibo, edituv_tris); - TEST_ASSIGN(IBO, ibo, edituv_lines); - TEST_ASSIGN(IBO, ibo, edituv_points); - TEST_ASSIGN(IBO, ibo, edituv_fdots); - - if (do_lines_loose_subbuffer) { - iter_flag |= MR_ITER_LEDGE; - } - -#undef TEST_ASSIGN - -#ifdef DEBUG_TIME - double rdata_start = PIL_check_seconds_timer(); -#endif - - MeshRenderData *mr = mesh_render_data_create(me, - is_editmode, - is_paint_mode, - is_mode_active, - obmat, - do_final, - do_uvedit, - cd_layer_used, - ts, - iter_flag, - data_flag); - mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; - mr->use_final_mesh = do_final; - -#ifdef DEBUG_TIME - double rdata_end = PIL_check_seconds_timer(); -#endif - - size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); - int32_t *task_counters = MEM_callocN(counters_size, __func__); - int counter_used = 0; - - struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_flag, data_flag); - ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN( - sizeof(ExtractSingleThreadedTaskData), __func__); - UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData), - __func__); - user_data_init_task_data->task_counters = task_counters; - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - -#define EXTRACT(buf, name) \ - if (mbc.buf.name) { \ - extract_task_create(task_graph, \ - task_node_mesh_render_data, \ - task_node_user_data_init, \ - &single_threaded_task_data->task_datas, \ - &user_data_init_task_data->task_datas, \ - scene, \ - mr, \ - cache, \ - &extract_##name, \ - mbc.buf.name, \ - &task_counters[counter_used++]); \ - } \ - ((void)0) - - EXTRACT(vbo, pos_nor); - EXTRACT(vbo, lnor); - EXTRACT(vbo, uv); - EXTRACT(vbo, tan); - EXTRACT(vbo, vcol); - EXTRACT(vbo, sculpt_data); - EXTRACT(vbo, orco); - EXTRACT(vbo, edge_fac); - EXTRACT(vbo, weights); - EXTRACT(vbo, edit_data); - EXTRACT(vbo, edituv_data); - EXTRACT(vbo, edituv_stretch_area); - EXTRACT(vbo, edituv_stretch_angle); - EXTRACT(vbo, mesh_analysis); - EXTRACT(vbo, fdots_pos); - EXTRACT(vbo, fdots_nor); - EXTRACT(vbo, fdots_uv); - EXTRACT(vbo, fdots_edituv_data); - EXTRACT(vbo, poly_idx); - EXTRACT(vbo, edge_idx); - EXTRACT(vbo, vert_idx); - EXTRACT(vbo, fdot_idx); - EXTRACT(vbo, skin_roots); - - EXTRACT(ibo, tris); - if (mbc.ibo.lines) { - /* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates - * the `lines_loose` sub-buffer. */ - const MeshExtract *lines_extractor = do_lines_loose_subbuffer ? - &extract_lines_with_lines_loose : - &extract_lines; - extract_task_create(task_graph, - task_node_mesh_render_data, - task_node_user_data_init, - &single_threaded_task_data->task_datas, - &user_data_init_task_data->task_datas, - scene, - mr, - cache, - lines_extractor, - mbc.ibo.lines, - &task_counters[counter_used++]); - } - else { - if (do_lines_loose_subbuffer) { - ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache); - BLI_addtail(&single_threaded_task_data->task_datas, taskdata); - } - } - EXTRACT(ibo, points); - EXTRACT(ibo, fdots); - EXTRACT(ibo, lines_paint_mask); - EXTRACT(ibo, lines_adjacency); - EXTRACT(ibo, edituv_tris); - EXTRACT(ibo, edituv_lines); - EXTRACT(ibo, edituv_points); - EXTRACT(ibo, edituv_fdots); - - /* Only create the edge when there is user data that needs to be initialized. - * The task is still part of the graph so the task_data will be freed when the graph is freed. - */ - if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) { - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); - } - - if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) { - struct TaskNode *task_node = extract_single_threaded_task_node_create( - task_graph, single_threaded_task_data); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - } - else { - extract_single_threaded_task_data_free(single_threaded_task_data); - } - - /* Trigger the sub-graph for this mesh. */ - BLI_task_graph_node_push_work(task_node_mesh_render_data); - -#undef EXTRACT - -#ifdef DEBUG_TIME - BLI_task_graph_work_and_wait(task_graph); - double end = PIL_check_seconds_timer(); - - static double avg = 0; - static double avg_fps = 0; - static double avg_rdata = 0; - static double end_prev = 0; - - if (end_prev == 0) { - end_prev = end; - } - - avg = avg * 0.95 + (end - rdata_end) * 0.05; - avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; - avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; - - printf( - "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); - - end_prev = end; -#endif -} - -/** \} */ + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h new file mode 100644 index 00000000000..6761282fe79 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -0,0 +1,514 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#pragma once + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_editmesh.h" + +#include "draw_cache_extract.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum eMRExtractType { + MR_EXTRACT_BMESH, + MR_EXTRACT_MAPPED, + MR_EXTRACT_MESH, +} eMRExtractType; + +typedef struct MeshRenderData { + eMRExtractType extract_type; + + int poly_len, edge_len, vert_len, loop_len; + int edge_loose_len; + int vert_loose_len; + int loop_loose_len; + int tri_len; + int mat_len; + + bool use_hide; + bool use_subsurf_fdots; + bool use_final_mesh; + + /** Use for #MeshStatVis calculation which use world-space coords. */ + float obmat[4][4]; + + const ToolSettings *toolsettings; + /** Edit Mesh */ + BMEditMesh *edit_bmesh; + BMesh *bm; + EditMeshData *edit_data; + + /* For deformed edit-mesh data. */ + /* Use for #ME_WRAPPER_TYPE_BMESH. */ + const float (*bm_vert_coords)[3]; + const float (*bm_vert_normals)[3]; + const float (*bm_poly_normals)[3]; + const float (*bm_poly_centers)[3]; + + int *v_origindex, *e_origindex, *p_origindex; + int crease_ofs; + int bweight_ofs; + int freestyle_edge_ofs; + int freestyle_face_ofs; + /** Mesh */ + Mesh *me; + const MVert *mvert; + const MEdge *medge; + const MLoop *mloop; + const MPoly *mpoly; + BMVert *eve_act; + BMEdge *eed_act; + BMFace *efa_act; + BMFace *efa_act_uv; + /* Data created on-demand (usually not for #BMesh based data). */ + MLoopTri *mlooptri; + float (*loop_normals)[3]; + float (*poly_normals)[3]; + int *lverts, *ledges; +} MeshRenderData; + +BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +{ + return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_face_at_index(mr->bm, mr->p_origindex[idx]) : + NULL; +} + +BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) +{ + return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : + NULL; +} + +BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) +{ + return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : + NULL; +} + +BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_coords)[3] = mr->bm_vert_coords; + if (vert_coords != NULL) { + return vert_coords[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->co; +} + +BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_normals)[3] = mr->bm_vert_normals; + if (vert_normals != NULL) { + return vert_normals[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->no; +} + +BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) +{ + const float(*poly_normals)[3] = mr->bm_poly_normals; + if (poly_normals != NULL) { + return poly_normals[BM_elem_index_get(efa)]; + } + + UNUSED_VARS(mr); + return efa->no; +} + +/* TODO(jbakker): phase out batch iteration macros as they are only used once. */ +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loop Triangles + * \{ */ + +typedef struct ExtractTriBMesh_Params { + BMLoop *(*looptris)[3]; + int tri_range[2]; +} ExtractTriBMesh_Params; +typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, + BMLoop **elt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 3) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } + +typedef struct ExtractTriMesh_Params { + const MLoopTri *mlooptri; + int tri_range[2]; +} ExtractTriMesh_Params; +typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, + const MLoopTri *mlt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 1) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Polygons, Loops + * \{ */ + +typedef struct ExtractPolyBMesh_Params { + BMLoop *(*looptris)[3]; + int poly_range[2]; +} ExtractPolyBMesh_Params; +typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, + BMFace *f, + const int f_index, + void *data); + +#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_poly = _ftable[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_BM_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_face = _ftable[index_poly]; \ + BMLoop *elem_loop, *l_first; \ + elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ + do { \ + const int index_loop = BM_elem_index_get(elem_loop); \ + (void)index_loop; /* Quiet warning when unused. */ + +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ + } \ + while ((elem_loop = elem_loop->next) != l_first) \ + ; \ + } \ + } + +typedef struct ExtractPolyMesh_Params { + int poly_range[2]; +} ExtractPolyMesh_Params; +typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data); + +#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_MESH_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ + elem_poly, index_poly, elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const MLoop *_mloop = mr->mloop; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ + for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ + const MLoop *elem_loop = &_mloop[index_loop]; \ + (void)elem_loop; + +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Edges + * \{ */ + +typedef struct ExtractLEdgeBMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeBMesh_Params; +typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ + CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ + BMEdge **_etable = mr->bm->etable; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLEdgeMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeMesh_Params; +typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ + CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ + { \ + const MEdge *_medge = mr->medge; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Vertices + * \{ */ + +typedef struct ExtractLVertBMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertBMesh_Params; +typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ + CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMVert **vtable = mr->bm->vtable; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + BMVert *elem_vert = vtable[lverts[index_lvert]]; \ + (void)elem_vert; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLVertMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertMesh_Params; +typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ + CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ + { \ + const MVert *mvert = mr->mvert; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + const MVert *elem = &mvert[lverts[index_lvert]]; \ + (void)elem; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +typedef void *(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *data); + +typedef struct MeshExtract { + /** Executed on main thread and return user data for iteration functions. */ + ExtractInitFn *init; + /** Executed on one (or more if use_threading) worker thread(s). */ + ExtractTriBMeshFn *iter_looptri_bm; + ExtractTriMeshFn *iter_looptri_mesh; + ExtractPolyBMeshFn *iter_poly_bm; + ExtractPolyMeshFn *iter_poly_mesh; + ExtractLEdgeBMeshFn *iter_ledge_bm; + ExtractLEdgeMeshFn *iter_ledge_mesh; + ExtractLVertBMeshFn *iter_lvert_bm; + ExtractLVertMeshFn *iter_lvert_mesh; + /** Executed on one worker thread after all elements iterations. */ + ExtractFinishFn *finish; + /** Used to request common data. */ + const eMRDataType data_type; + /** Used to know if the element callbacks are thread-safe and can be parallelized. */ + const bool use_threading; + /** + * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * buffer. + */ + const size_t mesh_buffer_offset; +} MeshExtract; + +/** \} */ + +/* draw_cache_extract_mesh_render_data.c */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const ToolSettings *ts, + const eMRIterType iter_type); +void mesh_render_data_free(MeshRenderData *mr); +void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag); +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag); + +/* draw_cache_extract_mesh_extractors.c */ +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc); +eMRIterType mesh_extract_iter_type(const MeshExtract *ext); +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals); +/* + * Total number of extractions types. + */ +#define M_EXTRACT_LEN 38 + +extern const MeshExtract extract_tris; +extern const MeshExtract extract_lines; +extern const MeshExtract extract_lines_with_lines_loose; +extern const MeshExtract extract_lines_loose_only; +extern const MeshExtract extract_points; +extern const MeshExtract extract_fdots; +extern const MeshExtract extract_lines_paint_mask; +extern const MeshExtract extract_lines_adjacency; +extern const MeshExtract extract_edituv_tris; +extern const MeshExtract extract_edituv_lines; +extern const MeshExtract extract_edituv_points; +extern const MeshExtract extract_edituv_fdots; +extern const MeshExtract extract_pos_nor; +extern const MeshExtract extract_pos_nor_hq; +extern const MeshExtract extract_lnor_hq; +extern const MeshExtract extract_lnor; +extern const MeshExtract extract_uv; +extern const MeshExtract extract_tan; +extern const MeshExtract extract_tan_hq; +extern const MeshExtract extract_sculpt_data; +extern const MeshExtract extract_vcol; +extern const MeshExtract extract_orco; +extern const MeshExtract extract_edge_fac; +extern const MeshExtract extract_weights; +extern const MeshExtract extract_edit_data; +extern const MeshExtract extract_edituv_data; +extern const MeshExtract extract_edituv_stretch_area; +extern const MeshExtract extract_edituv_stretch_angle; +extern const MeshExtract extract_mesh_analysis; +extern const MeshExtract extract_fdots_pos; +extern const MeshExtract extract_fdots_nor; +extern const MeshExtract extract_fdots_nor_hq; +extern const MeshExtract extract_fdots_uv; +extern const MeshExtract extract_fdots_edituv_data; +extern const MeshExtract extract_skin_roots; +extern const MeshExtract extract_poly_idx; +extern const MeshExtract extract_edge_idx; +extern const MeshExtract extract_vert_idx; +extern const MeshExtract extract_fdot_idx; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c new file mode 100644 index 00000000000..4741bcb06c9 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -0,0 +1,371 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" + +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_mesh.h" + +#include "GPU_batch.h" + +#include "ED_mesh.h" + +#include "draw_cache_extract_mesh_private.h" + +/* ---------------------------------------------------------------------- */ +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). + * \{ */ + +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) +{ + mr->ledges = cache->ledges; + mr->lverts = cache->lverts; + mr->vert_loose_len = cache->vert_loose_len; + mr->edge_loose_len = cache->edge_loose_len; + + mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); +} + +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges + * and verts are calculated at the same time.*/ + if (cache->lverts) { + return; + } + + cache->vert_loose_len = 0; + cache->edge_loose_len = 0; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->ledges[cache->edge_loose_len++] = med_index; + } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->lverts[cache->vert_loose_len++] = v; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + MEM_freeN(lvert_map); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->lverts[cache->vert_loose_len++] = elem_id; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->ledges[cache->edge_loose_len++] = elem_id; + } + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + } +} + +/** + * Part of the creation of the #MeshRenderData that happens in a thread. + */ +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); + BKE_mesh_recalc_looptri( + me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); + } + } + else { + /* #BMesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + /* Edit mode ensures this is valid, no need to calculate. */ + BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); + } + } +} + +void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag) +{ + Mesh *me = mr->me; + const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; + const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { + mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); + BKE_mesh_calc_normals_poly((MVert *)mr->mvert, + NULL, + mr->vert_len, + mr->mloop, + mr->mpoly, + mr->loop_len, + mr->poly_len, + mr->poly_normals, + true); + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); + BKE_mesh_normals_loop_split(mr->me->mvert, + mr->vert_len, + mr->me->medge, + mr->edge_len, + mr->me->mloop, + mr->loop_normals, + mr->loop_len, + mr->me->mpoly, + mr->poly_normals, + mr->poly_len, + is_auto_smooth, + split_angle, + NULL, + clnors, + NULL); + } + } + else { + /* #BMesh */ + if (data_flag & MR_DATA_POLY_NOR) { + /* Use #BMFace.no instead. */ + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + + const float(*vert_coords)[3] = NULL; + const float(*vert_normals)[3] = NULL; + const float(*poly_normals)[3] = NULL; + + if (mr->edit_data && mr->edit_data->vertexCos) { + vert_coords = mr->bm_vert_coords; + vert_normals = mr->bm_vert_normals; + poly_normals = mr->bm_poly_normals; + } + + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_loops_calc_normal_vcos(mr->bm, + vert_coords, + vert_normals, + poly_normals, + is_auto_smooth, + split_angle, + mr->loop_normals, + NULL, + NULL, + clnors_offset, + false); + } + } +} + +/** + * \param is_mode_active: When true, use the modifiers from the edit-data, + * otherwise don't use modifiers as they are not from this object. + */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const ToolSettings *ts, + const eMRIterType iter_type) +{ + MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); + mr->toolsettings = ts; + mr->mat_len = mesh_render_mat_len_get(me); + + copy_m4_m4(mr->obmat, obmat); + + if (is_editmode) { + BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); + mr->bm = me->edit_mesh->bm; + mr->edit_bmesh = me->edit_mesh; + mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; + + if (mr->edit_data) { + EditMeshData *emd = mr->edit_data; + if (emd->vertexCos) { + BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); + BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); + } + + mr->bm_vert_coords = mr->edit_data->vertexCos; + mr->bm_vert_normals = mr->edit_data->vertexNos; + mr->bm_poly_normals = mr->edit_data->polyNos; + mr->bm_poly_centers = mr->edit_data->polyCos; + } + + bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + bool use_mapped = is_mode_active && + (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); + + int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + + BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); + BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); + + mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); + mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); + mr->eed_act = BM_mesh_active_edge_get(mr->bm); + mr->eve_act = BM_mesh_active_vert_get(mr->bm); + + mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); + mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); +#ifdef WITH_FREESTYLE + mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); + mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); +#endif + + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; + + /* Seems like the mesh_eval_final do not have the right origin indices. + * Force not mapped in this case. */ + if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { + // mr->edit_bmesh = NULL; + mr->extract_type = MR_EXTRACT_MESH; + } + } + else { + mr->me = me; + mr->edit_bmesh = NULL; + + bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; + } + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + mr->vert_len = mr->me->totvert; + mr->edge_len = mr->me->totedge; + mr->loop_len = mr->me->totloop; + mr->poly_len = mr->me->totpoly; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + + mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); + mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); + mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); + mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); + + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + + mr->vert_len = bm->totvert; + mr->edge_len = bm->totedge; + mr->loop_len = bm->totloop; + mr->poly_len = bm->totface; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + } + + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } + + return mr; +} + +void mesh_render_data_free(MeshRenderData *mr) +{ + MEM_SAFE_FREE(mr->mlooptri); + MEM_SAFE_FREE(mr->poly_normals); + MEM_SAFE_FREE(mr->loop_normals); + + /* Loose geometry are owned by MeshBufferExtractionCache. */ + mr->ledges = NULL; + mr->lverts = NULL; + + MEM_freeN(mr); +} + +/** \} */
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 2c2ab9eaadd..5743f39f7da 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -266,7 +266,6 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(struct Object * struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit); - #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 223b44724b6..ee6a47e3dc6 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -25,8 +25,11 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" +#include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_span.hh" #include "BLI_utildefines.h" #include "DNA_curve_types.h" @@ -34,6 +37,8 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_font.h" +#include "BKE_geometry_set.hh" +#include "BKE_spline.hh" #include "GPU_batch.h" #include "GPU_capabilities.h" @@ -48,6 +53,11 @@ #include "draw_cache_impl.h" /* own include */ +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::Span; + /* See: edit_curve_point_vert.glsl for duplicate includes. */ #define SELECT 1 #define ACTIVE_NURB (1 << 2) @@ -139,6 +149,21 @@ static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cac } } +static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval, + int *r_curve_len, + int *r_vert_len, + int *r_edge_len) +{ + Span<SplinePtr> splines = curve_eval.splines(); + *r_curve_len = splines.size(); + *r_vert_len = 0; + *r_edge_len = 0; + for (const SplinePtr &spline : splines) { + *r_vert_len += spline->evaluated_points_size(); + *r_edge_len += spline->evaluated_edges_size(); + } +} + static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache) { int normal_len = 0; @@ -192,6 +217,9 @@ struct CurveRenderData { /* borrow from 'Object' */ CurveCache *ob_curve_cache; + /* Owned by the evaluated object's geometry set (#geometry_set_eval). */ + const CurveEval *curve_eval; + /* borrow from 'Curve' */ ListBase *nurbs; @@ -230,11 +258,21 @@ static CurveRenderData *curve_render_data_create(Curve *cu, rdata->ob_curve_cache = ob_curve_cache; + rdata->curve_eval = cu->curve_eval; + if (types & CU_DATATYPE_WIRE) { - curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache, - &rdata->wire.curve_len, - &rdata->wire.vert_len, - &rdata->wire.edge_len); + if (rdata->curve_eval != nullptr) { + curve_eval_render_wire_verts_edges_len_get(*rdata->curve_eval, + &rdata->wire.curve_len, + &rdata->wire.vert_len, + &rdata->wire.edge_len); + } + else { + curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache, + &rdata->wire.curve_len, + &rdata->wire.vert_len, + &rdata->wire.edge_len); + } } if (cu->editnurb) { @@ -556,8 +594,6 @@ void DRW_curve_batch_cache_free(Curve *cu) /* GPUBatch cache usage. */ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos) { - BLI_assert(rdata->ob_curve_cache != nullptr); - static GPUVertFormat format = {0}; static struct { uint pos; @@ -570,30 +606,46 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv GPU_vertbuf_init_with_format(vbo_curves_pos, &format); GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len); - int v_idx = 0; - LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { - if (bl->nr <= 0) { - continue; - } - const int i_end = v_idx + bl->nr; - for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) { - GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec); + if (rdata->curve_eval != nullptr) { + const CurveEval &curve_eval = *rdata->curve_eval; + Span<SplinePtr> splines = curve_eval.splines(); + Array<int> offsets = curve_eval.evaluated_point_offsets(); + BLI_assert(offsets.last() == vert_len); + + for (const int i_spline : splines.index_range()) { + Span<float3> positions = splines[i_spline]->evaluated_positions(); + for (const int i_point : positions.index_range()) { + GPU_vertbuf_attr_set( + vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]); + } } } - LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - for (int i = 0; i < dl->nr; v_idx++, i++) { - GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]); + else { + BLI_assert(rdata->ob_curve_cache != nullptr); + + int v_idx = 0; + LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { + if (bl->nr <= 0) { + continue; + } + const int i_end = v_idx + bl->nr; + for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) { + GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec); } } + LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { + if (ELEM(dl->type, DL_SEGM, DL_POLY)) { + for (int i = 0; i < dl->nr; v_idx++, i++) { + GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]); + } + } + } + BLI_assert(v_idx == vert_len); } - BLI_assert(v_idx == vert_len); } static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines) { - BLI_assert(rdata->ob_curve_cache != nullptr); - const int vert_len = curve_render_data_wire_verts_len_get(rdata); const int edge_len = curve_render_data_wire_edges_len_get(rdata); const int curve_len = curve_render_data_wire_curve_len_get(rdata); @@ -603,34 +655,56 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c GPUIndexBufBuilder elb; GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len); - int v_idx = 0; - LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { - if (bl->nr <= 0) { - continue; - } - const bool is_cyclic = bl->poly != -1; - if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1)); - } - for (int i = 0; i < bl->nr; i++) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + i); + if (rdata->curve_eval != nullptr) { + const CurveEval &curve_eval = *rdata->curve_eval; + Span<SplinePtr> splines = curve_eval.splines(); + Array<int> offsets = curve_eval.evaluated_point_offsets(); + BLI_assert(offsets.last() == vert_len); + + for (const int i_spline : splines.index_range()) { + const int eval_size = splines[i_spline]->evaluated_points_size(); + if (splines[i_spline]->is_cyclic()) { + GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1); + } + for (const int i_point : IndexRange(eval_size)) { + GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point); + } + GPU_indexbuf_add_primitive_restart(&elb); } - GPU_indexbuf_add_primitive_restart(&elb); - v_idx += bl->nr; } - LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - const bool is_cyclic = dl->type == DL_POLY; + else { + BLI_assert(rdata->ob_curve_cache != nullptr); + + int v_idx = 0; + LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { + if (bl->nr <= 0) { + continue; + } + const bool is_cyclic = bl->poly != -1; if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1)); + GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1)); } - for (int i = 0; i < dl->nr; i++) { + for (int i = 0; i < bl->nr; i++) { GPU_indexbuf_add_generic_vert(&elb, v_idx + i); } GPU_indexbuf_add_primitive_restart(&elb); - v_idx += dl->nr; + v_idx += bl->nr; + } + LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { + if (ELEM(dl->type, DL_SEGM, DL_POLY)) { + const bool is_cyclic = dl->type == DL_POLY; + if (is_cyclic) { + GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1)); + } + for (int i = 0; i < dl->nr; i++) { + GPU_indexbuf_add_generic_vert(&elb, v_idx + i); + } + GPU_indexbuf_add_primitive_restart(&elb); + v_idx += dl->nr; + } } } + GPU_indexbuf_build_in_place(&elb, ibo_curve_lines); } @@ -769,6 +843,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2; int vbo_len_used = 0; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) +#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos)) { GPU_vertbuf_init_with_format(vbo_pos, &format_pos); GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity); @@ -789,6 +866,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity); } +#undef DRW_TEST_ASSIGN_VBO +#undef DRW_TEST_ASSIGN_IBO + int nu_id = 0; for (Nurb *nu = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) { const BezTriple *bezt = nu->bezt; @@ -796,7 +876,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, if (bezt) { for (int a = 0; a < nu->pntsu; a++, bezt++) { - if (bezt->hide == true) { + if (bezt->hide != 0) { continue; } const bool handle_selected = BEZT_ISSEL_ANY(bezt); @@ -831,7 +911,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, else if (bp) { int pt_len = nu->pntsu * nu->pntsv; for (int a = 0; a < pt_len; a++, bp++, vbo_len_used += 1) { - if (bp->hide == true) { + if (bp->hide != 0) { continue; } int u = (a % nu->pntsu); @@ -1070,8 +1150,11 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag); - /* DispLists */ - ListBase *lb = &rdata->ob_curve_cache->disp; + /* The object's curve cache can be empty (in one case because we use #CurveEval's cache instead), + * If so, point to an empty DispList list to avoid the need to check for null in the following + * functions. */ + ListBase empty_lb = {nullptr, nullptr}; + ListBase *lb = rdata->ob_curve_cache == nullptr ? &empty_lb : &rdata->ob_curve_cache->disp; /* Generate VBOs */ if (DRW_vbo_requested(cache->ordered.pos_nor)) { diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index d606f70db9e..ee16cb1a022 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -532,6 +532,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPUVertBufRaw uv_step = {0}; GPUVertBufRaw tan_step = {0}; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) { GPU_vertbuf_init_with_format(vbo_pos_nor, do_hq_normals ? &format_pos_nor_hq : &format_pos_nor); @@ -550,6 +552,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPU_vertbuf_attr_get_raw_data(vbo_tan, tan_id, &tan_step); } +#undef DRW_TEST_ASSIGN_VBO + BKE_displist_normals_add(lb); LISTBASE_FOREACH (const DispList *, dl, lb) { diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 49b5e0fecd3..bea9ba1122b 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -405,7 +405,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr if (cache->vbo == NULL) { /* Should be discarded together. */ BLI_assert(cache->vbo == NULL && cache->ibo == NULL); - BLI_assert(cache->stroke_batch == NULL && cache->stroke_batch == NULL); + BLI_assert(cache->fill_batch == NULL && cache->stroke_batch == NULL); /* TODO/PERF: Could be changed to only do it if needed. * For now it's simpler to assume we always need it * since multiple viewport could or could not need it. diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index fd28ac00186..6424b21666d 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -243,7 +243,8 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c GPUVertFormat format = {0}; GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format); + cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(&format, + GPU_USAGE_DEVICE_ONLY); /* Create a destination buffer for the transform feedback. Sized appropriately */ /* Those are points! not line segments. */ diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index af54b57b162..3cc71e47f28 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -707,6 +707,26 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) } } +static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache) +{ + GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; + GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; + for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { + GPU_VERTBUF_DISCARD_SAFE(vbos[i]); + } + for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { + GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); + } +} + +static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache) +{ + MEM_SAFE_FREE(extraction_cache->lverts); + MEM_SAFE_FREE(extraction_cache->ledges); + extraction_cache->edge_loose_len = 0; + extraction_cache->vert_loose_len = 0; +} + static void mesh_batch_cache_clear(Mesh *me) { MeshBatchCache *cache = me->runtime.batch_cache; @@ -714,16 +734,13 @@ static void mesh_batch_cache_clear(Mesh *me) return; } FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; - GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; - for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { - GPU_VERTBUF_DISCARD_SAFE(vbos[i]); - } - for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { - GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); - } + mesh_buffer_cache_clear(mbufcache); } + mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache); + for (int i = 0; i < cache->mat_len; i++) { GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]); } @@ -1160,25 +1177,25 @@ static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, M * some issues (See T77867 where we needed to disable this function in order to debug what was * happening in release builds). */ BLI_task_graph_work_and_wait(task_graph); - for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) { + for (int i = 0; i < MBC_BATCH_LEN; i++) { BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0)); } - for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_VBO_LEN; i++) { BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i])); } - for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_IBO_LEN; i++) { BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i])); } - for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_VBO_LEN; i++) { BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i])); } - for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_IBO_LEN; i++) { BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i])); } - for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_VBO_LEN; i++) { BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i])); } - for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_IBO_LEN; i++) { BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i])); } } @@ -1542,7 +1559,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->uv_cage, + &cache->uv_cage, + &cache->uv_cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1551,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, true, false, - &cache->cd_used, scene, ts, true); @@ -1560,7 +1577,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_cage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->cage, + &cache->cage, + &cache->cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1569,7 +1587,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, true); @@ -1577,7 +1594,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, - cache->final, + &cache->final, + &cache->final_extraction_cache, me, is_editmode, is_paint_mode, @@ -1586,7 +1604,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, true, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h index bfc714e5d6a..6e537a3bffa 100644 --- a/source/blender/draw/intern/draw_cache_inline.h +++ b/source/blender/draw/intern/draw_cache_inline.h @@ -40,10 +40,6 @@ (flag |= DRW_ibo_requested(ibo) ? (value) : 0) #endif -/* Test and assign NULL if test fails */ -#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) -#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) - BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch) { /* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index bca227a24e2..585e171adc5 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -36,15 +36,28 @@ #include "BKE_duplilist.h" #include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" #include "GPU_shader.h" +#include "GPU_texture.h" #include "GPU_vertex_buffer.h" #include "draw_hair_private.h" #ifndef __APPLE__ # define USE_TRANSFORM_FEEDBACK +# define USE_COMPUTE_SHADERS #endif +BLI_INLINE bool drw_hair_use_compute_shaders(void) +{ +#ifdef USE_COMPUTE_SHADERS + return GPU_compute_shader_support(); +#else + return false; +#endif +} + typedef enum ParticleRefineShader { PART_REFINE_CATMULL_ROM = 0, PART_REFINE_MAX_SHADER, @@ -71,38 +84,89 @@ static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in t extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_hair_refine_vert_glsl[]; +extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; -static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) +/* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */ +/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's. + * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */ +#ifdef USE_COMPUTE_SHADERS +static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) { - if (g_refine_shaders[sh]) { - return g_refine_shaders[sh]; - } - - char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); + GPUShader *sh = NULL; + sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl, + datatoc_common_hair_lib_glsl, + "#define HAIR_PHASE_SUBDIV\n", + __func__); + return sh; +} +#endif #ifdef USE_TRANSFORM_FEEDBACK +static GPUShader *hair_refine_shader_transform_feedback_create( + ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; - g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback( - vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); -#else - g_refine_shaders[sh] = DRW_shader_create(vert_with_lib, - NULL, - datatoc_gpu_shader_3D_smooth_color_frag_glsl, - "#define blender_srgb_to_framebuffer_space(a) a\n" - "#define HAIR_PHASE_SUBDIV\n" - "#define TF_WORKAROUND\n"); + sh = DRW_shader_create_with_transform_feedback( + shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); + MEM_freeN(shader_src); + + return sh; +} #endif - MEM_freeN(vert_with_lib); +static GPUShader *hair_refine_shader_transform_feedback_workaround_create( + ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); + sh = DRW_shader_create(shader_src, + NULL, + datatoc_gpu_shader_3D_smooth_color_frag_glsl, + "#define blender_srgb_to_framebuffer_space(a) a\n" + "#define HAIR_PHASE_SUBDIV\n" + "#define TF_WORKAROUND\n"); + MEM_freeN(shader_src); + + return sh; +} + +static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) +{ + if (g_refine_shaders[refinement]) { + return g_refine_shaders[refinement]; + } + +#ifdef USE_COMPUTE_SHADERS + if (drw_hair_use_compute_shaders()) { + g_refine_shaders[refinement] = hair_refine_shader_compute_create(refinement); + if (g_refine_shaders[refinement]) { + return g_refine_shaders[refinement]; + } + } +#endif + +#ifdef USE_TRANSFORM_FEEDBACK + g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_create(refinement); + if (g_refine_shaders[refinement]) { + return g_refine_shaders[refinement]; + } +#endif - return g_refine_shaders[sh]; + g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_workaround_create( + refinement); + return g_refine_shaders[refinement]; } void DRW_hair_init(void) { -#ifdef USE_TRANSFORM_FEEDBACK +#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) g_tf_pass = DRW_pass_create("Update Hair Pass", 0); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); @@ -125,6 +189,67 @@ void DRW_hair_init(void) } } +static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, + ParticleHairCache *cache, + const int subdiv) +{ + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); + DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); +} + +static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv) +{ + const int strands_len = cache->strands_len; + const int final_points_len = cache->final[subdiv].strands_res * strands_len; + if (final_points_len > 0) { + GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); + drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv); + DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf); + + const int max_strands_per_call = GPU_max_work_group_count(0); + int strands_start = 0; + while (strands_start < strands_len) { + int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); + DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); + DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); + strands_start += batch_strands_len; + } + } +} + +static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache *cache, + const int subdiv) +{ + const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len > 0) { + GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + +#ifdef USE_TRANSFORM_FEEDBACK + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( + tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); +#else + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + pr_call->next = g_tf_calls; + pr_call->vbo = cache->final[subdiv].proc_buf; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); +#endif + + drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + } +} + static ParticleHairCache *drw_hair_particle_cache_get( Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res) { @@ -140,32 +265,11 @@ static ParticleHairCache *drw_hair_particle_cache_get( } if (update) { - int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); - -#ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); -#else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); -#endif - - DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex); - DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex); - DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); - DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + if (drw_hair_use_compute_shaders()) { + drw_hair_particle_cache_update_compute(cache, subdiv); + } + else { + drw_hair_particle_cache_update_transform_feedback(cache, subdiv); } } return cache; @@ -367,9 +471,11 @@ void DRW_hair_update(void) MEM_freeN(data); GPU_framebuffer_free(fb); #else - /* TODO(fclem): replace by compute shader. */ - /* Just render using transform feedback. */ + /* Just render the pass when using compute shaders or transform feedback. */ DRW_draw_pass(g_tf_pass); + if (drw_hair_use_compute_shaders()) { + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } #endif } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 84bc0327aa2..d4e22c83798 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -187,6 +187,10 @@ typedef enum { DRW_CMD_DRAW_INSTANCE = 2, DRW_CMD_DRAW_INSTANCE_RANGE = 3, DRW_CMD_DRAW_PROCEDURAL = 4, + + /* Compute Commands. */ + DRW_CMD_COMPUTE = 8, + /* Other Commands */ DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, @@ -224,6 +228,12 @@ typedef struct DRWCommandDrawInstanceRange { uint inst_count; } DRWCommandDrawInstanceRange; +typedef struct DRWCommandCompute { + int groups_x_len; + int groups_y_len; + int groups_z_len; +} DRWCommandCompute; + typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -260,6 +270,7 @@ typedef union DRWCommand { DRWCommandDrawInstance instance; DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; + DRWCommandCompute compute; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; DRWCommandSetSelectID select_id; @@ -274,6 +285,7 @@ struct DRWCallBuffer { }; /** Used by #DRWUniform.type */ +/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */ typedef enum { DRW_UNIFORM_INT = 0, DRW_UNIFORM_INT_COPY, @@ -286,6 +298,7 @@ typedef enum { DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, + DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, /** Per drawcall uniforms/UBO */ DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 6bdc5305fed..3b852e7f8c8 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -47,6 +47,7 @@ #endif #include "GPU_buffers.h" +#include "GPU_capabilities.h" #include "GPU_material.h" #include "GPU_uniform_buffer.h" @@ -446,6 +447,19 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, } } +void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + BLI_assert(false && "Unable to locate binding of shader storage buffer objects."); + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -700,6 +714,17 @@ static void drw_command_draw_intance_range( cmd->inst_count = count; } +static void drw_command_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len) +{ + DRWCommandCompute *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE); + cmd->groups_x_len = groups_x_len; + cmd->groups_y_len = groups_y_len; + cmd->groups_z_len = groups_z_len; +} + static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -815,6 +840,17 @@ void DRW_shgroup_call_instance_range( drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); } +void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len) +{ + BLI_assert(groups_x_len > 0 && groups_y_len > 0 && groups_z_len > 0); + BLI_assert(GPU_compute_shader_support()); + + drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len); +} + static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 4c8fcb0e016..f29caebeb84 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -29,6 +29,7 @@ #include "BKE_global.h" +#include "GPU_compute.h" #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_state.h" @@ -672,6 +673,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE: + GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location); + break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: state->baseinst_loc = uni->location; @@ -1050,6 +1054,12 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->instance_range.inst_count, false); break; + case DRW_CMD_COMPUTE: + GPU_compute_dispatch(shgroup->shader, + cmd->compute.groups_x_len, + cmd->compute.groups_y_len, + cmd->compute.groups_z_len); + break; } } diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 2aad1f10154..83d0030f89b 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -396,6 +396,7 @@ GPUShader *DRW_shader_create_with_transform_feedback(const char *vert, datatoc_gpu_shader_depth_only_frag_glsl, geom, NULL, + NULL, defines, prim_type, varying_names, diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 8684d82f228..02c335ddae2 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -28,6 +28,9 @@ uniform bool hairCloseTip = true; uniform vec4 hairDupliMatrix[4]; +/* Strand batch offset when used in compute shaders. */ +uniform int hairStrandOffset = 0; + /* -- Per control points -- */ uniform samplerBuffer hairPointBuffer; /* RGBA32F */ #define point_position xyz @@ -43,13 +46,37 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */ /* -- Subdivision stage -- */ /** - * We use a transform feedback to preprocess the strands and add more subdivision to it. - * For the moment these are simple smooth interpolation but one could hope to see the full + * We use a transform feedback or compute shader to preprocess the strands and add more subdivision + * to it. For the moment these are simple smooth interpolation but one could hope to see the full * children particle modifiers being evaluated at this stage. * * If no more subdivision is needed, we can skip this step. */ +#ifdef GPU_VERTEX_SHADER +float hair_get_local_time() +{ + return float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); +} + +int hair_get_id() +{ + return gl_VertexID / hairStrandsRes; +} +#endif + +#ifdef GPU_COMPUTE_SHADER +float hair_get_local_time() +{ + return float(gl_GlobalInvocationID.y) / float(hairStrandsRes - 1); +} + +int hair_get_id() +{ + return int(gl_GlobalInvocationID.x) + hairStrandOffset; +} +#endif + #ifdef HAIR_PHASE_SUBDIV int hair_get_base_id(float local_time, int strand_segments, out float interp_time) { @@ -64,9 +91,9 @@ int hair_get_base_id(float local_time, int strand_segments, out float interp_tim void hair_get_interp_attrs( out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time) { - float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); + float local_time = hair_get_local_time(); - int hair_id = gl_VertexID / hairStrandsRes; + int hair_id = hair_get_id(); int strand_offset = int(texelFetch(hairStrandBuffer, hair_id).x); int strand_segments = int(texelFetch(hairStrandSegBuffer, hair_id).x); @@ -96,6 +123,7 @@ void hair_get_interp_attrs( */ #if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER) + int hair_get_strand_id(void) { return gl_VertexID / (hairStrandsRes * hairThicknessRes); @@ -227,3 +255,45 @@ vec2 hair_resolve_barycentric(vec2 vert_barycentric) return vec2(1.0 - vert_barycentric.x, 0.0); } } + +/* Hair interpolation functions. */ +vec4 hair_get_weights_cardinal(float t) +{ + float t2 = t * t; + float t3 = t2 * t; +#if defined(CARDINAL) + float fc = 0.71; +#else /* defined(CATMULL_ROM) */ + float fc = 0.5; +#endif + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + float fct = t * fc; + float fct2 = t2 * fc; + float fct3 = t3 * fc; + weights.x = (fct2 * 2.0 - fct3) - fct; + weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; + weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; + weights.w = fct3 - fct2; + return weights; +} + +/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ +vec4 hair_get_weights_bspline(float t) +{ + float t2 = t * t; + float t3 = t2 * t; + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); + weights.y = (0.5 * t3 - t2 + 0.66666666); + weights.w = (0.16666666 * t3); + return weights; +} + +vec4 hair_interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) +{ + return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; +} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl new file mode 100644 index 00000000000..4dcde4b0245 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl @@ -0,0 +1,24 @@ + +/* + * To be compiled with common_hair_lib.glsl. + */ + +layout(local_size_x = 1, local_size_y = 1) in; +layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer +{ + vec4 posTime[]; +} +out_vertbuf; + +void main(void) +{ + float interp_time; + vec4 data0, data1, data2, data3; + hair_get_interp_attrs(data0, data1, data2, data3, interp_time); + + vec4 weights = hair_get_weights_cardinal(interp_time); + vec4 result = hair_interp_data(data0, data1, data2, data3, weights); + + uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y; + out_vertbuf.posTime[index] = result; +} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl index 3f5e3f8226f..371d43827b9 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl @@ -3,47 +3,6 @@ out vec4 finalColor; -vec4 get_weights_cardinal(float t) -{ - float t2 = t * t; - float t3 = t2 * t; -#if defined(CARDINAL) - float fc = 0.71; -#else /* defined(CATMULL_ROM) */ - float fc = 0.5; -#endif - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - float fct = t * fc; - float fct2 = t2 * fc; - float fct3 = t3 * fc; - weights.x = (fct2 * 2.0 - fct3) - fct; - weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; - weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; - weights.w = fct3 - fct2; - return weights; -} - -/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ -vec4 get_weights_bspline(float t) -{ - float t2 = t * t; - float t3 = t2 * t; - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); - weights.y = (0.5 * t3 - t2 + 0.66666666); - weights.w = (0.16666666 * t3); - return weights; -} - -vec4 interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) -{ - return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; -} - #ifdef TF_WORKAROUND uniform int targetWidth; uniform int targetHeight; @@ -56,8 +15,8 @@ void main(void) vec4 data0, data1, data2, data3; hair_get_interp_attrs(data0, data1, data2, data3, interp_time); - vec4 weights = get_weights_cardinal(interp_time); - finalColor = interp_data(data0, data1, data2, data3, weights); + vec4 weights = hair_get_weights_cardinal(interp_time); + finalColor = hair_interp_data(data0, data1, data2, data3, weights); #ifdef TF_WORKAROUND int id = gl_VertexID - idOffset; diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 33deae0b0a1..479f9cd1827 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -86,6 +86,8 @@ float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } +float safe_sqrt(float a) { return sqrt(max(a, 0.0)); } + float sqr(float a) { return a * a; } vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 8842274e017..e4f2de1f741 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -210,7 +210,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_i, depth, r_location_world)) { + if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) { is_location_world_set = true; if (r_normal_world) { zero_v3(r_normal_world); diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c index 12f6bb90677..b2ecee90a57 100644 --- a/source/blender/editors/geometry/geometry_attributes.c +++ b/source/blender/editors/geometry/geometry_attributes.c @@ -52,12 +52,16 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + if (C == NULL) { + return DummyRNA_NULL_items; + } + Object *ob = ED_object_context(C); - if (ob != NULL) { - return rna_enum_attribute_domain_itemf(ob->data, r_free); + if (ob == NULL) { + return DummyRNA_NULL_items; } - return DummyRNA_NULL_items; + return rna_enum_attribute_domain_itemf(ob->data, r_free); } static int geometry_attribute_add_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index f7ab72a8a43..bff7310e9f7 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC gpencil_add_monkey.c gpencil_add_stroke.c gpencil_armature.c + gpencil_bake_animation.c gpencil_convert.c gpencil_data.c gpencil_edit.c diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c new file mode 100644 index 00000000000..30ebc9189c5 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -0,0 +1,448 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_anim_data.h" +#include "BKE_context.h" +#include "BKE_duplilist.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_transform_snap_object_context.h" + +#include "gpencil_intern.h" + +const EnumPropertyItem rna_gpencil_reproject_type_items[] = { + {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, + {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, + {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, + {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, + {GP_REPROJECT_VIEW, + "VIEW", + 0, + "View", + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_CURSOR, + "CURSOR", + 0, + "Cursor", + "Reproject the strokes using the orientation of 3D cursor"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* Check frame_end is always > start frame! */ +static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int frame_start = RNA_int_get(ptr, "frame_start"); + int frame_end = RNA_int_get(ptr, "frame_end"); + + if (frame_end <= frame_start) { + RNA_int_set(ptr, "frame_end", frame_start + 1); + } +} + +/* Extract mesh animation to Grease Pencil. */ +static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { + return false; + } + + /* Check if grease pencil or empty for dupli groups. */ + if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) { + return false; + } + + /* Only if the current view is 3D View. */ + ScrArea *area = CTX_wm_area(C); + return (area && area->spacetype); +} + +typedef struct GpBakeOb { + struct GpBakeOb *next, *prev; + Object *ob; +} GpBakeOb; + +/* Get list of keyframes used by selected objects. */ +static void animdata_keyframe_list_get(ListBase *ob_list, + const bool only_selected, + GHash *r_keyframes) +{ + /* Loop all objects to get the list of keyframes used. */ + LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { + Object *ob = elem->ob; + AnimData *adt = BKE_animdata_from_id(&ob->id); + if ((adt == NULL) || (adt->action == NULL)) { + continue; + } + LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { + int i; + BezTriple *bezt; + for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { + /* Keyframe number is x value of point. */ + if ((bezt->f2 & SELECT) || (!only_selected)) { + /* Insert only one key for each keyframe number. */ + int key = (int)bezt->vec[1][0]; + if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { + BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } +} + +static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) +{ + GpBakeOb *elem = NULL; + ListBase *lb; + DupliObject *dob; + lb = object_duplilist(depsgraph, scene, ob); + for (dob = lb->first; dob; dob = dob->next) { + if (dob->ob->type != OB_GPENCIL) { + continue; + } + + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = dob->ob; + BLI_addtail(list, elem); + } + + free_object_duplilist(lb); +} + +static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) +{ + GpBakeOb *elem = NULL; + + /* Add active object. In some files this could not be in selected array. */ + Object *obact = CTX_data_active_object(C); + + if (obact->type == OB_GPENCIL) { + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = obact; + BLI_addtail(list, elem); + } + /* Add duplilist. */ + else if (obact->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, obact, list); + } + + /* Add other selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob == obact) { + continue; + } + /* Add selected objects.*/ + if (ob->type == OB_GPENCIL) { + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = ob; + BLI_addtail(list, elem); + } + + /* Add duplilist. */ + if (ob->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, ob, list); + } + } + CTX_DATA_END; +} + +static void gpencil_bake_free_ob_list(ListBase *list) +{ + LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { + MEM_SAFE_FREE(elem); + } +} + +static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + + ListBase ob_selected_list = {NULL, NULL}; + gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); + + /* Grab all relevant settings. */ + const int step = RNA_int_get(op->ptr, "step"); + + const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? + scene->r.sfra : + RNA_int_get(op->ptr, "frame_start"); + + const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? + scene->r.efra : + RNA_int_get(op->ptr, "frame_end"); + + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; + const int project_type = RNA_enum_get(op->ptr, "project_type"); + + /* Create a new grease pencil object. */ + Object *ob_gpencil = NULL; + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits); + float invmat[4][4]; + invert_m4_m4(invmat, ob_gpencil->obmat); + + bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data; + gpd_dst->draw_mode = GP_DRAWMODE_2D; + + /* Set cursor to indicate working. */ + WM_cursor_wait(true); + + GP_SpaceConversion gsc = {NULL}; + SnapObjectContext *sctx = NULL; + if (project_type != GP_REPROJECT_KEEP) { + /* Init space conversion stuff. */ + gpencil_point_conversion_init(C, &gsc); + /* Move the grease pencil object to conversion data. */ + gsc.ob = ob_gpencil; + + /* Init snap context for geometry projection. */ + sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + } + + /* Loop all frame range. */ + int oldframe = (int)DEG_get_ctime(depsgraph); + int key = -1; + + /* Get list of keyframes. */ + GHash *keyframe_list = BLI_ghash_int_new(__func__); + if (only_selected) { + animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); + } + + for (int i = frame_start; i < frame_end + 1; i++) { + key++; + /* Jump if not step limit but include last frame always. */ + if ((key % step != 0) && (i != frame_end)) { + continue; + } + + /* Check if frame is in the list of frames to be exported. */ + if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + continue; + } + + /* Move scene to new frame. */ + CFRA = i; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Loop all objects in the list. */ + LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { + Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); + bGPdata *gpd_src = ob_eval->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { + /* Create destination layer. */ + char *layer_name; + layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info); + bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name); + if (gpl_dst == NULL) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false); + } + MEM_freeN(layer_name); + + /* Layer Transform matrix. */ + float matrix[4][4]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); + + /* Duplicate frame. */ + bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV); + if (gpf_src == NULL) { + continue; + } + bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true); + gpf_dst->framenum = CFRA + frame_offset; + gpf_dst->flag &= ~GP_FRAME_SELECT; + BLI_addtail(&gpl_dst->frames, gpf_dst); + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) { + /* Create material of the stroke. */ + Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1); + bool found = false; + for (int index = 0; index < ob_gpencil->totcol; index++) { + Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1); + if (ma_src == ma_dst) { + found = true; + break; + } + } + if (!found) { + BKE_object_material_slot_add(bmain, ob_gpencil); + BKE_object_material_assign( + bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF); + } + + /* Set new material index. */ + gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src); + + /* Update point location to new object space. */ + for (int j = 0; j < gps->totpoints; j++) { + bGPDspoint *pt = &gps->points[j]; + mul_m4_v3(matrix, &pt->x); + mul_m4_v3(invmat, &pt->x); + } + + /* Reproject stroke. */ + if (project_type != GP_REPROJECT_KEEP) { + ED_gpencil_stroke_reproject( + depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false); + } + else { + BKE_gpencil_stroke_geometry_update(gpd_dst, gps); + } + } + } + } + } + /* Return scene frame state and DB to original state. */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Free memory. */ + gpencil_bake_free_ob_list(&ob_selected_list); + if (sctx != NULL) { + ED_transform_snap_object_context_destroy(sctx); + } + /* Free temp hash table. */ + if (keyframe_list != NULL) { + BLI_ghash_free(keyframe_list, NULL, NULL); + } + + /* Notifiers. */ + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* Reset cursor. */ + WM_cursor_wait(false); + + /* done */ + return OPERATOR_FINISHED; +} + +static int gpencil_bake_grease_pencil_animation_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + + prop = RNA_struct_find_property(op->ptr, "frame_start"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_start = RNA_property_int_get(op->ptr, prop); + if (frame_start < scene->r.sfra) { + RNA_property_int_set(op->ptr, prop, scene->r.sfra); + } + } + + prop = RNA_struct_find_property(op->ptr, "frame_end"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_end = RNA_property_int_get(op->ptr, prop); + if (frame_end > scene->r.efra) { + RNA_property_int_set(op->ptr, prop, scene->r.efra); + } + } + + /* Show popup dialog to allow editing. */ + return WM_operator_props_dialog_popup(C, op, 250); +} + +void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Bake Object Transform to Grease Pencil"; + ot->idname = "GPENCIL_OT_bake_grease_pencil_animation"; + ot->description = "Bake grease pencil object transform to grease pencil keyframes"; + + /* callbacks */ + ot->invoke = gpencil_bake_grease_pencil_animation_invoke; + ot->exec = gpencil_bake_grease_pencil_animation_exec; + ot->poll = gpencil_bake_grease_pencil_animation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int( + ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); + + prop = RNA_def_int( + ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); + RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end); + + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); + + RNA_def_boolean( + ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes"); + RNA_def_int( + ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); + + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_KEEP, + "Projection Type", + ""); +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e65afa1abff..e01221db277 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -5347,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Normalize Operator + * \{ */ + +typedef enum eGP_NormalizeMode { + GP_NORMALIZE_THICKNESS = 0, + GP_NORMALIZE_OPACITY, +} eGP_NormalizeMode; + +static bool gpencil_stroke_normalize_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return false; + } + + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + + return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL)); +} + +static void gpencil_stroke_normalize_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE); + + if (mode == GP_NORMALIZE_THICKNESS) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE); + } + else if (mode == GP_NORMALIZE_OPACITY) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE); + } +} + +static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* Sanity checks. */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + const int value = RNA_int_get(op->ptr, "value"); + const float factor = RNA_float_get(op->ptr, "factor"); + + /* Go through each editable + selected stroke. */ + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + if (gpf == NULL) { + continue; + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT : + (gps->flag & GP_STROKE_SELECT); + if (!selected) { + continue; + } + + float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1); + /* Fill opacity need to be managed before. */ + if (mode == GP_NORMALIZE_OPACITY) { + gps->fill_opacity_fac = factor; + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); + } + + /* Loop all Polyline points. */ + if (!is_curve_edit) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + pt->strength = factor; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + } + else { + /* Loop all Bezier points. */ + for (int i = 0; i < gps->editcurve->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + gpc_pt->strength = factor; + CLAMP(gpc_pt->strength, 0.0f, 1.0f); + } + } + + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + } + /* If not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_normalize(wmOperatorType *ot) +{ + static const EnumPropertyItem prop_gpencil_normalize_modes[] = { + {GP_NORMALIZE_THICKNESS, + "THICKNESS", + 0, + "Thickness", + "Normalizes the stroke thickness by making all points use the same thickness value"}, + {GP_NORMALIZE_OPACITY, + "OPACITY", + 0, + "Opacity", + "Normalizes the stroke opacity by making all points use the same opacity value"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Normalize Stroke"; + ot->idname = "GPENCIL_OT_stroke_normalize"; + ot->description = "Normalize stroke attributes"; + + /* api callbacks */ + ot->exec = gpencil_stroke_normalize_exec; + ot->poll = gpencil_stroke_normalize_poll; + ot->ui = gpencil_stroke_normalize_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum( + ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized"); + RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000); +} + +/** \} */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 9f48c81c8f1..276e8c81e0f 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -444,6 +444,7 @@ void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot); +void GPENCIL_OT_bake_grease_pencil_animation(struct wmOperatorType *ot); void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot); void GPENCIL_OT_trace_image(struct wmOperatorType *ot); @@ -487,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot); void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot); void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot); void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot); @@ -750,4 +752,7 @@ struct GP_EditableStrokes_Iter { } \ (void)0 +/* Reused items for bake operators. */ +extern const EnumPropertyItem rna_gpencil_reproject_type_items[]; + /* ****************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index b7ed77801c0..55468dffab0 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -402,25 +402,6 @@ static int gpencil_bake_mesh_animation_invoke(bContext *C, void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) { - static const EnumPropertyItem reproject_type[] = { - {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, - {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, - {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, - {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, - {GP_REPROJECT_VIEW, - "VIEW", - 0, - "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " - "using 'Cursor' Stroke Placement"}, - {GP_REPROJECT_CURSOR, - "CURSOR", - 0, - "Cursor", - "Reproject the strokes using the orientation of 3D cursor"}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem target_object_modes[] = { {GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""}, {GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""}, @@ -491,5 +472,10 @@ void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) RNA_def_int( ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); - RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_VIEW, + "Projection Type", + ""); } diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 698d784c3b0..35640cf3b66 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -622,6 +622,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_convert); WM_operatortype_append(GPENCIL_OT_bake_mesh_animation); + WM_operatortype_append(GPENCIL_OT_bake_grease_pencil_animation); WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil); #ifdef WITH_POTRACE @@ -648,6 +649,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance); WM_operatortype_append(GPENCIL_OT_stroke_merge_material); WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color); + WM_operatortype_append(GPENCIL_OT_stroke_normalize); WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4de97411059..ea3d921f2c5 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -42,6 +42,7 @@ struct SpaceImage; struct ToolSettings; struct ViewLayer; struct bNode; +struct bNodeTree; struct wmKeyConfig; /* uvedit_ops.c */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 66ec57c8a31..52d69d12253 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -162,10 +162,10 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, const int mval[2], float r_normal[3]); -bool ED_view3d_depth_unproject(const struct ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]); +bool ED_view3d_depth_unproject_v3(const struct ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]); void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ @@ -410,8 +410,13 @@ void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, const float obmat[4][4], float r_pmat[4][4]); -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]); -bool ED_view3d_unproject( +void ED_view3d_project_v3(const struct ARegion *region, + const float world[3], + float r_region_co[3]); +void ED_view3d_project_v2(const struct ARegion *region, + const float world[3], + float r_region_co[2]); +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]); /* end */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index f26a85ac93f..61511a78925 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -3228,19 +3228,17 @@ void ui_but_range_set_hard(uiBut *but) const PropertyType type = RNA_property_type(but->rnaprop); - /* clamp button range to something reasonable in case - * we get -inf/inf from RNA properties */ if (type == PROP_INT) { int imin, imax; RNA_property_int_range(&but->rnapoin, but->rnaprop, &imin, &imax); - but->hardmin = (imin == INT_MIN) ? -1e4 : imin; - but->hardmax = (imin == INT_MAX) ? 1e4 : imax; + but->hardmin = imin; + but->hardmax = imax; } else if (type == PROP_FLOAT) { float fmin, fmax; RNA_property_float_range(&but->rnapoin, but->rnaprop, &fmin, &fmax); - but->hardmin = (fmin == -FLT_MAX) ? (float)-1e4 : fmin; - but->hardmax = (fmax == FLT_MAX) ? (float)1e4 : fmax; + but->hardmin = fmin; + but->hardmax = fmax; } } diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index e4f502950ab..b52bfc81b7a 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -138,8 +138,8 @@ void eyedropper_draw_cursor_text_region(const struct bContext *C, } const int mval[2] = { - x - region->winrct.xmin, - y - region->winrct.ymin, + x - region->winrct.xmin, + y - region->winrct.ymin, }; eyedropper_draw_cursor_text_ex(mval[0], mval[1], name); diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index d5fb0e4e744..6583fa31dbf 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -118,7 +118,8 @@ static bool eyedropper_init(bContext *C, wmOperator *op) RNA_property_float_get_array(&eye->ptr, eye->prop, col); if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { eye->crypto_node = (bNode *)eye->ptr.data; - eye->cryptomatte_session = ntreeCompositCryptomatteSession(eye->crypto_node); + eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C), + eye->crypto_node); eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye); } @@ -199,6 +200,57 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay return false; } +static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Scene *scene = (Scene *)node->id; + BLI_assert(GS(scene->id.name) == ID_SCE); + Render *re = RE_GetSceneRender(scene); + + if (re) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + RE_ReleaseResult(re); + } + return success; +} + +static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, + NodeCryptomatte *crypto, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Image *image = (Image *)node->id; + BLI_assert(GS(image->id.name) == ID_IM); + ImageUser *iuser = &crypto->iuser; + + if (image && image->type == IMA_TYPE_MULTILAYER) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + if (image->rr) { + LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + BKE_image_release_ibuf(image, ibuf, NULL); + } + return success; +} static bool eyedropper_cryptomatte_sample_fl( bContext *C, Eyedropper *eye, int mx, int my, float r_col[3]) @@ -255,53 +307,19 @@ static bool eyedropper_cryptomatte_sample_fl( return false; } - bool success = false; /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */ char prefix[MAX_NAME + 1]; - ntreeCompositCryptomatteLayerPrefix(node, prefix, sizeof(prefix) - 1); + const Scene *scene = CTX_data_scene(C); + ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); prefix[MAX_NAME] = '\0'; if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - Scene *scene = (Scene *)node->id; - BLI_assert(GS(scene->id.name) == ID_SCE); - Render *re = RE_GetSceneRender(scene); - - if (re) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - RE_ReleaseResult(re); - } + return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } - else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { - Image *image = (Image *)node->id; - BLI_assert(GS(image->id.name) == ID_IM); - ImageUser *iuser = &crypto->iuser; - - if (image && image->type == IMA_TYPE_MULTILAYER) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); - if (image->rr) { - LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - BKE_image_release_ibuf(image, ibuf, NULL); - } + if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { + return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); } - - return success; + return false; } /** diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 633d2234449..e4e641b474c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2395,16 +2395,16 @@ static int get_but_property_array_length(uiBut *but) } static void ui_but_set_float_array( - bContext *C, uiBut *but, uiHandleButtonData *data, float *values, int array_length) + bContext *C, uiBut *but, uiHandleButtonData *data, const float *values, const int values_len) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - for (int i = 0; i < array_length; i++) { + for (int i = 0; i < values_len; i++) { RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]); } if (data) { if (but->type == UI_BTYPE_UNITVEC) { - BLI_assert(array_length == 3); + BLI_assert(values_len == 3); copy_v3_v3(data->vec, values); } else { @@ -2415,56 +2415,39 @@ static void ui_but_set_float_array( button_activate_state(C, but, BUTTON_STATE_EXIT); } -static void float_array_to_string(float *values, - int array_length, +static void float_array_to_string(const float *values, + const int values_len, char *output, int output_len_max) { - /* to avoid buffer overflow attacks; numbers are quite arbitrary */ - BLI_assert(output_len_max > 15); - output_len_max -= 10; - - int current_index = 0; - output[current_index] = '['; - current_index++; - - for (int i = 0; i < array_length; i++) { - int length = BLI_snprintf( - output + current_index, output_len_max - current_index, "%f", values[i]); - current_index += length; - - if (i < array_length - 1) { - if (current_index < output_len_max) { - output[current_index + 0] = ','; - output[current_index + 1] = ' '; - current_index += 2; - } - } + const int values_end = values_len - 1; + int ofs = 0; + output[ofs++] = '['; + for (int i = 0; i < values_len; i++) { + ofs += BLI_snprintf_rlen( + output + ofs, output_len_max - ofs, (i != values_end) ? "%f, " : "%f]", values[i]); } - - output[current_index + 0] = ']'; - output[current_index + 1] = '\0'; } static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max) { - const int array_length = get_but_property_array_length(but); - float *values = alloca(array_length * sizeof(float)); + const int values_len = get_but_property_array_length(but); + float *values = alloca(values_len * sizeof(float)); RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values); - float_array_to_string(values, array_length, output, output_len_max); + float_array_to_string(values, values_len, output, output_len_max); } -static bool parse_float_array(char *text, float *values, int expected_length) +static bool parse_float_array(char *text, float *values, int values_len_expected) { /* can parse max 4 floats for now */ - BLI_assert(0 <= expected_length && expected_length <= 4); + BLI_assert(0 <= values_len_expected && values_len_expected <= 4); float v[5]; - const int actual_length = sscanf( + const int values_len_actual = sscanf( text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]); - if (actual_length == expected_length) { - memcpy(values, v, sizeof(float) * expected_length); + if (values_len_actual == values_len_expected) { + memcpy(values, v, sizeof(float) * values_len_expected); return true; } return false; @@ -2475,16 +2458,16 @@ static void ui_but_paste_numeric_array(bContext *C, uiHandleButtonData *data, char *buf_paste) { - const int array_length = get_but_property_array_length(but); - if (array_length > 4) { + const int values_len = get_but_property_array_length(but); + if (values_len > 4) { /* not supported for now */ return; } - float *values = alloca(sizeof(float) * array_length); + float *values = alloca(sizeof(float) * values_len); - if (parse_float_array(buf_paste, values, array_length)) { - ui_but_set_float_array(C, but, data, values, array_length); + if (parse_float_array(buf_paste, values, values_len)) { + ui_but_set_float_array(C, but, data, values, values_len); } else { WM_report(RPT_ERROR, "Expected an array of numbers: [n, n, ...]"); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index f19666e56b0..26ac27ca8bb 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -653,7 +653,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* Only remap that specific ID usage to overriding local data-block. */ ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); if (override_id != NULL) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); if (GS(override_id->name) == ID_OB) { Scene *scene = CTX_data_scene(C); @@ -672,7 +672,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } else { if (BKE_lib_id_make_local(bmain, id, false, 0)) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* reassign to get get proper updates/notifiers */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); @@ -1078,7 +1078,7 @@ static void template_ID(const bContext *C, char numstr[32]; short numstr_len; - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id)); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id)); but = uiDefBut( block, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index b5cd9c7f60d..825b7d11aef 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -578,8 +578,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, float r_origin_ofs[3]) { /* unproject to find view ray */ - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); /* transform into object space */ mul_m4_v3(kcd->ob_imat, r_origin); @@ -1745,7 +1745,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, float view[3], p_ofs[3]; /* TODO: I think there's a simpler way to get the required raycast ray */ - ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view); + ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view); mul_m4_v3(kcd->ob_imat, view); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 79385e28aa9..112de68b52c 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -93,6 +93,12 @@ typedef struct BArrayCustomData { #endif typedef struct UndoMesh { + /** + * This undo-meshes in `um_arraystore.local_links`. + * Not to be confused with the next and previous undo steps. + */ + struct UndoMesh *local_next, *local_prev; + Mesh me; int selectmode; @@ -128,7 +134,10 @@ static struct { struct BArrayStore_AtSize bs_stride; int users; - /* We could have the undo API pass in the previous state, for now store a local list */ + /** + * A list of #UndoMesh items ordered from oldest to newest + * used to access previous undo data for a mesh. + */ ListBase local_links; # ifdef USE_ARRAY_STORE_THREAD @@ -520,11 +529,63 @@ static void um_arraystore_free(UndoMesh *um) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Array Store Utilities + * \{ */ + +/** + * Create an array of #UndoMesh from `objects`. + * + * where each element in the resulting array is the most recently created + * undo-mesh for the object's mesh. + * When no undo-mesh can be found that array index is NULL. + * + * This is used for de-duplicating memory between undo steps, + * failure to find the undo step will store a full duplicate in memory. + * define `DEBUG_PRINT` to check memory is de-duplicating as expected. + */ +static UndoMesh **mesh_undostep_reference_elems_from_objects(Object **object, int object_len) +{ + /* Map: `Mesh.id.session_uuid` -> `UndoMesh`. */ + GHash *uuid_map = BLI_ghash_ptr_new_ex(__func__, object_len); + UndoMesh **um_references = MEM_callocN(sizeof(UndoMesh *) * object_len, __func__); + for (int i = 0; i < object_len; i++) { + const Mesh *me = object[i]->data; + BLI_ghash_insert(uuid_map, POINTER_FROM_INT(me->id.session_uuid), &um_references[i]); + } + int uuid_map_len = object_len; + + /* Loop backwards over all previous mesh undo data until either: + * - All elements have been found (where `um_references` we'll have every element set). + * - There are no undo steps left to look for. */ + UndoMesh *um_iter = um_arraystore.local_links.last; + while (um_iter && (uuid_map_len != 0)) { + UndoMesh **um_p; + if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) { + *um_p = um_iter; + uuid_map_len--; + } + um_iter = um_iter->local_prev; + } + BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map)); + BLI_ghash_free(uuid_map, NULL, NULL); + if (uuid_map_len == object_len) { + MEM_freeN(um_references); + um_references = NULL; + } + return um_references; +} + +/** \} */ + #endif /* USE_ARRAY_STORE */ /* for callbacks */ /* undo simply makes copies of a bmesh */ -static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) +/** + * \param um_ref: The reference to use for de-duplicating memory between undo-steps. + */ +static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, UndoMesh *um_ref) { BLI_assert(BLI_array_is_zeroed(um, 1)); #ifdef USE_ARRAY_STORE_THREAD @@ -560,14 +621,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) #ifdef USE_ARRAY_STORE { - /* We could be more clever here, - * the previous undo state may be from a separate mesh. */ - const UndoMesh *um_ref = um_arraystore.local_links.last ? - ((LinkData *)um_arraystore.local_links.last)->data : - NULL; - /* Add ourselves. */ - BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um)); + BLI_addtail(&um_arraystore.local_links, um); # ifdef USE_ARRAY_STORE_THREAD if (um_arraystore.task_pool == NULL) { @@ -583,6 +638,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) um_arraystore_compact_with_info(um, um_ref); # endif } +#else + UNUSED_VARS(um_ref); #endif return um; @@ -682,11 +739,9 @@ static void undomesh_free_data(UndoMesh *um) /* we need to expand so any allocations in custom-data are freed with the mesh */ um_arraystore_expand(um); - { - LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data)); - BLI_remlink(&um_arraystore.local_links, link); - MEM_freeN(link); - } + BLI_assert(BLI_findindex(&um_arraystore.local_links, um) != -1); + BLI_remlink(&um_arraystore.local_links, um); + um_arraystore_free(um); #endif @@ -720,7 +775,6 @@ static Object *editmesh_object_from_context(bContext *C) * \{ */ typedef struct MeshUndoStep_Elem { - struct MeshUndoStep_Elem *next, *prev; UndoRefID_Object obedit_ref; UndoMesh data; } MeshUndoStep_Elem; @@ -749,6 +803,12 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; + UndoMesh **um_references = NULL; + +#ifdef USE_ARRAY_STORE + um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len); +#endif + for (uint i = 0; i < objects_len; i++) { Object *ob = objects[i]; MeshUndoStep_Elem *elem = &us->elems[i]; @@ -756,12 +816,22 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und elem->obedit_ref.ptr = ob; Mesh *me = elem->obedit_ref.ptr->data; BMEditMesh *em = me->edit_mesh; - undomesh_from_editmesh(&elem->data, me->edit_mesh, me->key); + undomesh_from_editmesh( + &elem->data, me->edit_mesh, me->key, um_references ? um_references[i] : NULL); em->needs_flush_to_id = 1; us->step.data_size += elem->data.undo_size; + +#ifdef USE_ARRAY_STORE + /** As this is only data storage it is safe to set the session ID here. */ + elem->data.me.id.session_uuid = me->id.session_uuid; +#endif } MEM_freeN(objects); + if (um_references != NULL) { + MEM_freeN(um_references); + } + bmain->is_memfile_undo_flush_needed = true; return true; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8cf7d60e6c4..ef500be0133 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1312,6 +1312,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const int type = RNA_enum_get(op->ptr, "type"); const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); + const bool use_lights = RNA_boolean_get(op->ptr, "use_lights"); const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order"); ushort local_view_bits; @@ -1432,6 +1433,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) id_us_plus(&md->target_material->id); } + if (use_lights) { + ob->dtx |= OB_USE_GPENCIL_LIGHTS; + } + else { + ob->dtx &= ~OB_USE_GPENCIL_LIGHTS; + } + /* Stroke object is drawn in front of meshes by default. */ if (use_in_front) { ob->dtx |= OB_DRAW_IN_FRONT; @@ -1474,6 +1482,7 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) int type = RNA_enum_get(op->ptr, "type"); if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) { + uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); uiLayout *row = uiLayoutRow(layout, false); @@ -1520,6 +1529,8 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) false, "In Front", "Show line art grease pencil in front of everything"); + RNA_def_boolean( + ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object"); RNA_def_enum( ot->srna, "stroke_depth_order", @@ -2137,8 +2148,7 @@ static void copy_object_set_idnew(bContext *C) FOREACH_MAIN_ID_END; #endif - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } /** \} */ @@ -2459,7 +2469,7 @@ static void make_object_duplilist_real(bContext *C, free_object_duplilist(lb_duplis); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); base->object->transflag &= ~OB_DUPLI; DEG_id_tag_update(&base->object->id, ID_RECALC_COPY_ON_WRITE); @@ -2474,7 +2484,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op) const bool use_base_parent = RNA_boolean_get(op->ptr, "use_base_parent"); const bool use_hierarchy = RNA_boolean_get(op->ptr, "use_hierarchy"); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) { make_object_duplilist_real(C, depsgraph, scene, base, use_base_parent, use_hierarchy); @@ -3359,7 +3369,7 @@ Base *ED_object_add_duplicate( DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); } - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); return basen; } @@ -3375,8 +3385,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); CTX_DATA_BEGIN (C, Base *, base, selected_bases) { Base *basen = object_add_duplicate_internal( diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index dcb294bcb12..3d1a5ac2d62 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -30,6 +30,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "BLT_translation.h" #include "BKE_context.h" @@ -419,7 +421,7 @@ static bool object_transfer_mode_poll(bContext *C) return false; } const Object *ob = CTX_data_active_object(C); - return ob && (ob->mode & (OB_MODE_SCULPT)); + return ob && (ob->mode != OB_MODE_OBJECT); } /* Update the viewport rotation origin to the mouse cursor. */ @@ -438,6 +440,13 @@ static void object_transfer_mode_reposition_view_pivot(bContext *C, const int mv ups->last_stroke_valid = true; } +static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob_dst_eval = DEG_get_evaluated_object(depsgraph, ob_dst); + ob_dst_eval->runtime.overlay_mode_transfer_start_time = PIL_check_seconds_timer(); +} + static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base_dst) { Scene *scene = CTX_data_scene(C); @@ -475,6 +484,8 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base ob_dst_orig = DEG_get_original_object(ob_dst); ED_object_mode_set_ex(C, last_mode, true, op->reports); + object_overlay_mode_transfer_animation_start(C, ob_dst); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_toolsystem_update_from_context_view3d(C); mode_transfered = true; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b685a93f27b..2c188e310db 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1944,7 +1944,7 @@ void ED_object_single_user(Main *bmain, Scene *scene, Object *ob) ob->flag |= OB_DONE; single_object_users(bmain, scene, NULL, OB_DONE, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } static void single_obdata_users( @@ -2454,7 +2454,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, id_root, &obact->id); + bmain, scene, view_layer, id_root, &obact->id, NULL); /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ @@ -2644,7 +2644,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op) single_object_action_users(bmain, scene, view_layer, v3d, flag); } - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); WM_event_add_notifier(C, NC_WINDOW, NULL); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 9ef2cce875f..1ff576504ce 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -525,7 +525,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev float d_a[3], d_b[3]; float d_a_proj[2], d_b_proj[2]; - float preview_plane_proj[4][3]; + float preview_plane_proj[4][2]; const float y_axis_proj[2] = {0.0f, 1.0f}; mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]); @@ -534,7 +534,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev for (int i = 0; i < 4; i++) { float preview_plane_world_space[3]; mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]); - ED_view3d_project(region, preview_plane_world_space, preview_plane_proj[i]); + ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]); } /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index a87b5054efa..b9a3bc87e19 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1654,7 +1654,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x if (center_tot) { mul_v3_fl(center, 1.0f / center_tot); float center_proj[3]; - ED_view3d_project(xfd->vc.region, center, center_proj); + ED_view3d_project_v3(xfd->vc.region, center, center_proj); xfd->prev.depth = center_proj[2]; xfd->prev.is_depth_valid = true; } @@ -1890,7 +1890,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { xfd->prev.depth = depth_fl; xfd->prev.is_depth_valid = true; - if (ED_view3d_depth_unproject(region, event->mval, depth, location_world)) { + if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) { if (is_translate) { float normal[3]; diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 5f81f9afe4f..3f40d637188 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3985,6 +3985,10 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + if (C == NULL) { + return DummyRNA_NULL_items; + } + Object *ob = ED_object_context(C); EnumPropertyItem tmp = {0, "", 0, "", ""}; EnumPropertyItem *item = NULL; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5b545784e5b..97994b65f40 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -609,7 +609,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre } float win[3]; - ED_view3d_project(data->vc.region, co, win); + ED_view3d_project_v3(data->vc.region, co, win); if (win[2] - 0.00001f > depth) { return 0; diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 3829aeebbeb..7e111905883 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1039,7 +1039,7 @@ static void cursor_draw_point_screen_space(const uint gpuattr, float translation_vertex_cursor[3], location[3]; copy_v3_v3(location, true_location); mul_m4_v3(obmat, location); - ED_view3d_project(region, location, translation_vertex_cursor); + ED_view3d_project_v3(region, location, translation_vertex_cursor); /* Do not draw points behind the view. Z [near, far] is mapped to [-1, 1]. */ if (translation_vertex_cursor[2] <= 1.0f) { imm_draw_circle_fill_3d( diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b6ae6f8bee7..da34723eed4 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1239,10 +1239,9 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) })); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3]; looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); BMIter iter; int i; @@ -1290,7 +1289,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) break; } BM_mesh_boolean( - bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); + bm, looptris, looptris_tot, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); } MEM_freeN(looptris); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index b093f07226e..59a5ad63f0e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -846,7 +846,7 @@ static int paint_space_stroke(bContext *C, while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( C, scene, stroke, pressure, dpressure, length); - float mouse[3]; + float mouse[2]; if (length >= spacing) { if (use_scene_spacing) { @@ -856,7 +856,7 @@ static int paint_space_stroke(bContext *C, add_v3_v3v3(final_world_space_position, stroke->last_world_space_position, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -1240,7 +1240,7 @@ static void paint_line_strokes_spacing(bContext *C, mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final); add_v3_v3v3( final_world_space_position, world_space_position_old, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 2e1dd928f96..d6d54a1985d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1854,7 +1854,7 @@ static void flip_v3(float v[3], const ePaintSymmetryFlags symm) flip_v3_v3(v, v, symm); } -static void flip_qt(float quat[3], const ePaintSymmetryFlags symm) +static void flip_qt(float quat[4], const ePaintSymmetryFlags symm) { flip_qt_qt(quat, quat, symm); } @@ -4196,7 +4196,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], } } -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 087cb6dd94a..e2ee4c9fed3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -278,7 +278,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index d555238e949..7379891543b 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -809,12 +809,12 @@ void uiTemplateMovieclipInformation(uiLayout *layout, char str[1024]; size_t ofs = 0; - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height); if (ibuf) { if (ibuf->rect_float) { if (ibuf->channels != 4) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_(", %d float channel(s)"), ibuf->channels); } else if (ibuf->planes == R_IMF_PLANES_RGBA) { @@ -837,7 +837,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout, short frs_sec; float frs_sec_base; if (IMB_anim_get_fps(clip->anim, &frs_sec, &frs_sec_base, true)) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_(", %.2f fps"), (float)frs_sec / frs_sec_base); } } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 3868ad5eb35..81dce30c695 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1744,7 +1744,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) /* directory change */ if (file && (file->typeflag & FILE_TYPE_DIR)) { if (!file->relpath) { - return OPERATOR_CANCELLED; + return false; } if (FILENAME_IS_PARENT(file->relpath)) { @@ -1783,7 +1783,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC); } - return OPERATOR_FINISHED; + return true; } static int file_exec(bContext *C, wmOperator *UNUSED(op)) @@ -2260,23 +2260,24 @@ void FILE_OT_filepath_drop(wmOperatorType *ot) * \{ */ /** - * Create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created. + * Create a new, non-existing folder name, returns true if successful, + * false if name couldn't be created. * The actual name is returned in 'name', 'folder' contains the complete path, * including the new folder name. */ -static int new_folder_path(const char *parent, char *folder, char *name) +static bool new_folder_path(const char *parent, char folder[FILE_MAX], char name[FILE_MAXFILE]) { int i = 1; int len = 0; BLI_strncpy(name, "New Folder", FILE_MAXFILE); - BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */ + BLI_join_dirfile(folder, FILE_MAX, parent, name); /* check whether folder with the name already exists, in this case * add number to the name. Check length of generated name to avoid * crazy case of huge number of folders each named 'New Folder (x)' */ while (BLI_exists(folder) && (len < FILE_MAXFILE)) { len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i); - BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */ + BLI_join_dirfile(folder, FILE_MAX, parent, name); i++; } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 6fb64de7e85..d909bfd1864 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1218,11 +1218,12 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i const int len = MAX_IMAGE_INFO_LEN; int ofs = 0; - ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y); + ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y); if (ibuf->rect_float) { if (ibuf->channels != 4) { - ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels); + ofs += BLI_snprintf_rlen( + str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels); } else if (ibuf->planes == R_IMF_PLANES_RGBA) { ofs += BLI_strncpy_rlen(str + ofs, TIP_(" RGBA float"), len - ofs); diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 0bdfceb36b6..cf847fa18a8 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -505,14 +505,14 @@ static void get_stats_string( LayerCollection *layer_collection = view_layer->active_collection; if (object_mode == OB_MODE_OBJECT) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - "%s | ", - BKE_collection_ui_name_get(layer_collection->collection)); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + "%s | ", + BKE_collection_ui_name_get(layer_collection->collection)); } if (ob) { - *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2); + *ofs += BLI_snprintf_rlen(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2); } if (obedit) { @@ -521,72 +521,72 @@ static void get_stats_string( } if (obedit->type == OB_MESH) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), - stats_fmt->totvertsel, - stats_fmt->totvert, - stats_fmt->totedgesel, - stats_fmt->totedge, - stats_fmt->totfacesel, - stats_fmt->totface, - stats_fmt->tottri); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), + stats_fmt->totvertsel, + stats_fmt->totvert, + stats_fmt->totedgesel, + stats_fmt->totedge, + stats_fmt->totfacesel, + stats_fmt->totface, + stats_fmt->tottri); } else if (obedit->type == OB_ARMATURE) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Joints:%s/%s | Bones:%s/%s"), - stats_fmt->totvertsel, - stats_fmt->totvert, - stats_fmt->totbonesel, - stats_fmt->totbone); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Joints:%s/%s | Bones:%s/%s"), + stats_fmt->totvertsel, + stats_fmt->totvert, + stats_fmt->totbonesel, + stats_fmt->totbone); } else { - *ofs += BLI_snprintf( + *ofs += BLI_snprintf_rlen( info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert); } } else if (ob && (object_mode & OB_MODE_POSE)) { - *ofs += BLI_snprintf( + *ofs += BLI_snprintf_rlen( info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone); } else if ((ob) && (ob->type == OB_GPENCIL)) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), - stats_fmt->totgplayer, - stats_fmt->totgpframe, - stats_fmt->totgpstroke, - stats_fmt->totgppoint); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), + stats_fmt->totgplayer, + stats_fmt->totgpframe, + stats_fmt->totgpstroke, + stats_fmt->totgppoint); } else if (ob && (object_mode & OB_MODE_SCULPT)) { if (stats_is_object_dynamic_topology_sculpt(ob)) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s | Tris:%s"), - stats_fmt->totvert, - stats_fmt->tottri); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s | Tris:%s"), + stats_fmt->totvert, + stats_fmt->tottri); } else { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s/%s | Faces:%s/%s"), - stats_fmt->totvertsculpt, - stats_fmt->totvert, - stats_fmt->totfacesculpt, - stats_fmt->totface); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s/%s | Faces:%s/%s"), + stats_fmt->totvertsculpt, + stats_fmt->totvert, + stats_fmt->totfacesculpt, + stats_fmt->totface); } } else { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s | Faces:%s | Tris:%s"), - stats_fmt->totvert, - stats_fmt->totface, - stats_fmt->tottri); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s | Faces:%s | Tris:%s"), + stats_fmt->totvert, + stats_fmt->totface, + stats_fmt->tottri); } - *ofs += BLI_snprintf( + *ofs += BLI_snprintf_rlen( info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj); } @@ -613,11 +613,11 @@ static const char *info_statusbar_string(Main *bmain, /* Memory status. */ if (statusbar_flag & STATUSBAR_SHOW_MEMORY) { if (info[0]) { - ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | "); } uintptr_t mem_in_use = MEM_get_memory_in_use(); BLI_str_format_byte_unit(formatted_mem, mem_in_use, false); - ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem); + ofs += BLI_snprintf_rlen(info + ofs, len, TIP_("Memory: %s"), formatted_mem); } /* GPU VRAM status. */ @@ -627,27 +627,27 @@ static const char *info_statusbar_string(Main *bmain, float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f; float gpu_free_gb = gpu_free_mem_kb / 1048576.0f; if (info[0]) { - ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | "); } if (gpu_free_mem_kb && gpu_tot_mem_kb) { - ofs += BLI_snprintf(info + ofs, - len - ofs, - TIP_("VRAM: %.1f/%.1f GiB"), - gpu_total_gb - gpu_free_gb, - gpu_total_gb); + ofs += BLI_snprintf_rlen(info + ofs, + len - ofs, + TIP_("VRAM: %.1f/%.1f GiB"), + gpu_total_gb - gpu_free_gb, + gpu_total_gb); } else { /* Can only show amount of GPU VRAM available. */ - ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb); } } /* Blender version. */ if (statusbar_flag & STATUSBAR_SHOW_VERSION) { if (info[0]) { - ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | "); } - ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string()); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string()); } return info; diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index bc043a4e665..6e234c5b2ce 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -37,20 +37,20 @@ set(INC set(SRC - drawnode.c - node_add.c + drawnode.cc + node_add.cc node_buttons.c node_draw.cc - node_edit.c + node_edit.cc node_geometry_attribute_search.cc node_gizmo.c - node_group.c + node_group.cc node_ops.c - node_relationships.c - node_select.c - node_templates.c - node_toolbar.c - node_view.c + node_relationships.cc + node_select.cc + node_templates.cc + node_toolbar.cc + node_view.cc space_node.c node_intern.h diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.cc index fb02086e26c..6137d34fd0c 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.cc @@ -95,9 +95,9 @@ static void node_socket_button_label(bContext *UNUSED(C), static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; /* first output stores value */ - bNodeSocket *output = node->outputs.first; + bNodeSocket *output = (bNodeSocket *)node->outputs.first; PointerRNA sockptr; RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); @@ -106,15 +106,15 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; /* first output stores value */ - bNodeSocket *output = node->outputs.first; + bNodeSocket *output = (bNodeSocket *)node->outputs.first; PointerRNA sockptr; uiLayout *col; RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); col = uiLayoutColumn(layout, false); - uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0); + uiTemplateColorPicker(col, &sockptr, "default_value", true, false, false, false); uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); } @@ -129,14 +129,14 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA uiItemR(row, ptr, "use_alpha", DEFAULT_FLAGS, "", ICON_IMAGE_RGB_ALPHA); } - uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { #if 0 /* XXX no context access here .. */ - bNode *node = ptr->data; + bNode *node = (bNode*)ptr->data; CurveMapping *cumap = node->storage; if (cumap) { @@ -156,7 +156,7 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiTemplateColorRamp(layout, ptr, "color_ramp", 0); + uiTemplateColorRamp(layout, ptr, "color_ramp", false); } static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -179,8 +179,8 @@ void ED_node_sample_set(const float col[4]) static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; - CurveMapping *cumap = node->storage; + bNode *node = (bNode *)ptr->data; + CurveMapping *cumap = (CurveMapping *)node->storage; if (_sample_col[0] != SAMPLE_FLT_ISNONE) { cumap->flag |= CUMA_DRAW_SAMPLE; @@ -198,9 +198,9 @@ static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; /* first output stores normal */ - bNodeSocket *output = node->outputs.first; + bNodeSocket *output = (bNodeSocket *)node->outputs.first; PointerRNA sockptr; RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); @@ -209,7 +209,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA * static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; short multi = (node->id && ((Tex *)node->id)->use_nodes && (node->type != CMP_NODE_TEXTURE) && (node->type != TEX_NODE_TEXTURE)); @@ -233,14 +233,14 @@ static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), Po if (!ELEM(RNA_enum_get(ptr, "interpolation_type"), NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP)) { - uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } } static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } static int node_resize_area_default(bNode *node, int x, int y) @@ -274,7 +274,7 @@ static int node_resize_area_default(bNode *node, int x, int y) static void node_draw_buttons_group(uiLayout *layout, bContext *C, PointerRNA *ptr) { uiTemplateIDBrowse( - layout, C, ptr, "node_tree", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, NULL); + layout, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr); } /* XXX Does a bounding box update by iterating over all children. @@ -311,7 +311,7 @@ static void node_draw_frame_prepare(const bContext *UNUSED(C), bNodeTree *ntree, /* first child initializes frame */ if (bbinit) { - bbinit = 0; + bbinit = false; rect = noderect; data->flag &= ~NODE_FRAME_RESIZEABLE; } @@ -418,9 +418,9 @@ static void node_draw_frame(const bContext *C, { /* skip if out of view */ - if (BLI_rctf_isect(&node->totr, ®ion->v2d.cur, NULL) == false) { + if (BLI_rctf_isect(&node->totr, ®ion->v2d.cur, nullptr) == false) { UI_block_end(C, node->block); - node->block = NULL; + node->block = nullptr; return; } @@ -462,7 +462,7 @@ static void node_draw_frame(const bContext *C, UI_block_end(C, node->block); UI_block_draw(C, node->block); - node->block = NULL; + node->block = nullptr; } static int node_resize_area_frame(bNode *node, int x, int y) @@ -497,7 +497,7 @@ static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA { uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE); uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE); - uiItemR(layout, ptr, "text", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "text", DEFAULT_FLAGS, nullptr, ICON_NONE); } #define NODE_REROUTE_SIZE 8.0f @@ -511,11 +511,11 @@ static void node_draw_reroute_prepare(const bContext *UNUSED(C), node_to_view(node, 0.0f, 0.0f, &locx, &locy); /* reroute node has exactly one input and one output, both in the same place */ - bNodeSocket *nsock = node->outputs.first; + bNodeSocket *nsock = (bNodeSocket *)node->outputs.first; nsock->locx = locx; nsock->locy = locy; - nsock = node->inputs.first; + nsock = (bNodeSocket *)node->inputs.first; nsock->locx = locx; nsock->locy = locy; @@ -541,7 +541,7 @@ static void node_draw_reroute(const bContext *C, if (node->totr.xmax < region->v2d.cur.xmin || node->totr.xmin > region->v2d.cur.xmax || node->totr.ymax < region->v2d.cur.ymin || node->totr.ymin > region->v2d.cur.ymax) { UI_block_end(C, node->block); - node->block = NULL; + node->block = nullptr; return; } @@ -586,12 +586,12 @@ static void node_draw_reroute(const bContext *C, (int)(rct->ymax), (short)512, (short)NODE_DY, - NULL, + nullptr, 0, 0, 0, 0, - NULL); + nullptr); } /* only draw input socket. as they all are placed on the same position. @@ -601,7 +601,7 @@ static void node_draw_reroute(const bContext *C, UI_block_end(C, node->block); UI_block_draw(C, node->block); - node->block = NULL; + node->block = nullptr; } /* Special tweak area for reroute node. @@ -612,7 +612,7 @@ static int node_tweak_area_reroute(bNode *node, int x, int y) /* square of tweak radius */ const float tweak_radius_sq = square_f(24.0f); - bNodeSocket *sock = node->inputs.first; + bNodeSocket *sock = (bNodeSocket *)node->inputs.first; float dx = sock->locx - x; float dy = sock->locy - y; return (dx * dx + dy * dy <= tweak_radius_sq); @@ -662,28 +662,28 @@ static void node_buts_image_user(uiLayout *layout, /* don't use iuser->framenr directly * because it may not be updated if auto-refresh is off */ Scene *scene = CTX_data_scene(C); - ImageUser *iuser = iuserptr->data; + ImageUser *iuser = (ImageUser *)iuserptr->data; /* Image *ima = imaptr->data; */ /* UNUSED */ char numstr[32]; - const int framenr = BKE_image_user_frame_get(iuser, CFRA, NULL); + const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr); BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr); uiItemL(layout, numstr, ICON_NONE); } if (ELEM(source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, nullptr, ICON_NONE); } if (show_layer_selection && RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && RNA_boolean_get(ptr, "has_layers")) { col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "layer", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "layer", DEFAULT_FLAGS, nullptr, ICON_NONE); } if (show_color_management) { @@ -693,7 +693,7 @@ static void node_buts_image_user(uiLayout *layout, uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); /* Avoid losing changes image is painted. */ - if (BKE_image_is_dirty(imaptr->data)) { + if (BKE_image_is_dirty((Image *)imaptr->data)) { uiLayoutSetEnabled(split, false); } } @@ -701,13 +701,13 @@ static void node_buts_image_user(uiLayout *layout, static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0); } static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -717,7 +717,7 @@ static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), Po static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(layout, ptr, "convert_from", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "convert_to", DEFAULT_FLAGS, "", ICON_NONE); } @@ -730,7 +730,7 @@ static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), Po static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, nullptr, 0); } static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -745,10 +745,10 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA "image", "IMAGE_OT_new", "IMAGE_OT_open", - NULL, + nullptr, UI_TEMPLATE_ID_FILTER_ALL, false, - NULL); + nullptr); uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); @@ -767,7 +767,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA static void node_shader_buts_tex_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false); } static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -782,10 +782,10 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin "image", "IMAGE_OT_new", "IMAGE_OT_open", - NULL, + nullptr, UI_TEMPLATE_ID_FILTER_ALL, false, - NULL); + nullptr); uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); @@ -796,7 +796,7 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false); uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, IFACE_("Interpolation"), ICON_NONE); uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE); @@ -808,33 +808,33 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), Poin if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) { uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE); } if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) { uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, nullptr, ICON_NONE); } if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { - uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, nullptr, 0); uiLayout *col; if (RNA_boolean_get(ptr, "sun_disc")) { col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, nullptr, ICON_NONE); } col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, nullptr, ICON_NONE); } } @@ -845,7 +845,7 @@ static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -901,32 +901,33 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; - NodeShaderTexPointDensity *shader_point_density = node->storage; + bNode *node = (bNode *)ptr->data; + NodeShaderTexPointDensity *shader_point_density = (NodeShaderTexPointDensity *)node->storage; Object *ob = (Object *)node->id; PointerRNA ob_ptr, obdata_ptr; RNA_id_pointer_create((ID *)ob, &ob_ptr); - RNA_id_pointer_create(ob ? (ID *)ob->data : NULL, &obdata_ptr); + RNA_id_pointer_create(ob ? (ID *)ob->data : nullptr, &obdata_ptr); - uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, ICON_NONE); if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { PointerRNA dataptr; RNA_id_pointer_create((ID *)node->id, &dataptr); - uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE); + uiItemPointerR( + layout, ptr, "particle_system", &dataptr, "particle_systems", nullptr, ICON_NONE); } - uiItemR(layout, ptr, "space", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, nullptr, ICON_NONE); if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { - uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE); } else { - uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE); if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) { if (ob_ptr.data) { uiItemPointerR( @@ -944,18 +945,18 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout, static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, 0); - uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, 0); + uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0); } static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0); } static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0); if (!RNA_boolean_get(ptr, "from_instancer")) { PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); @@ -989,7 +990,7 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, nullptr, 0); } static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1036,7 +1037,7 @@ static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA * } } else { - uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, 0); + uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, 0); } } @@ -1083,7 +1084,7 @@ static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerR uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(layout, true); @@ -1100,7 +1101,7 @@ static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), Point uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(layout, true); @@ -1122,7 +1123,7 @@ static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA #if 0 /* not implemented yet */ if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) { - uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, nullptr, ICON_NONE); } #endif } @@ -1139,21 +1140,21 @@ static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), Po col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, true); uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_shader_buts_ambient_occlusion(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1163,7 +1164,7 @@ static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "name", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "name", DEFAULT_FLAGS, nullptr, ICON_NONE); } /* only once called */ @@ -1348,17 +1349,17 @@ static void node_buts_image_views(uiLayout *layout, if (RNA_boolean_get(ptr, "has_views")) { if (RNA_enum_get(ptr, "view") == 0) { - uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_CAMERA_STEREO); + uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_CAMERA_STEREO); } else { - uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_SCENE); + uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_SCENE); } } } static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; PointerRNA iuserptr; RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); @@ -1369,10 +1370,10 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * "image", "IMAGE_OT_new", "IMAGE_OT_open", - NULL, + nullptr, UI_TEMPLATE_ID_FILTER_ALL, false, - NULL); + nullptr); if (!node->id) { return; } @@ -1386,20 +1387,29 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; PointerRNA iuserptr; RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, true); } static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; uiLayout *col, *row; - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (!node->id) { return; @@ -1423,7 +1433,7 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer PointerRNA op_ptr; uiItemFullO( - row, "RENDER_OT_render", "", ICON_RENDER_STILL, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + row, "RENDER_OT_render", "", ICON_RENDER_STILL, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); RNA_string_set(&op_ptr, "layer", layer_name); RNA_string_set(&op_ptr, "scene", scene_name); } @@ -1438,19 +1448,19 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(col, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); if (filter != R_FILTER_FAST_GAUSS) { - uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE); if (!reference) { - uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, nullptr, ICON_NONE); } - uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE); } - uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_boolean_get(ptr, "use_relative")) { uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "factor_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); @@ -1461,15 +1471,15 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(col, ptr, "size_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); uiItemR(col, ptr, "size_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); } - uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col; - uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, true); uiItemL(col, IFACE_("Center:"), ICON_NONE); @@ -1479,13 +1489,13 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin uiItemS(layout); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE); uiItemS(layout); - uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_bilateralblur(uiLayout *layout, @@ -1495,9 +1505,9 @@ static void node_composit_buts_bilateralblur(uiLayout *layout, uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1507,27 +1517,36 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA col = uiLayoutColumn(layout, false); uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE); uiItemR(col, ptr, "bokeh", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true); - uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, nullptr, ICON_NONE); sub = uiLayoutColumn(col, false); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false); - uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1536,9 +1555,9 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "threshold", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "contrast_limit", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "corner_rounding", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "threshold", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "contrast_limit", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); } /* qdn: glare node */ @@ -1548,29 +1567,30 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin uiItemR(layout, ptr, "quality", DEFAULT_FLAGS, "", ICON_NONE); if (RNA_enum_get(ptr, "glare_type") != 1) { - uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "glare_type") != 0) { - uiItemR(layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR( + layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } } - uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "glare_type") == 2) { - uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, nullptr, ICON_NONE); } if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) { - uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "glare_type") == 0) { - uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, nullptr, ICON_NONE); } } if (RNA_enum_get(ptr, "glare_type") == 1) { - uiItemR(layout, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE); } } @@ -1581,15 +1601,15 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "tonemap_type", DEFAULT_FLAGS, "", ICON_NONE); if (RNA_enum_get(ptr, "tonemap_type") == 0) { - uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE); } else { - uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } } @@ -1598,12 +1618,12 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(col, false); uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false); - uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1611,7 +1631,7 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE); uiItemR(col, ptr, "factor", DEFAULT_FLAGS, IFACE_("Blur"), ICON_NONE); col = uiLayoutColumn(layout, true); @@ -1619,7 +1639,7 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(col, ptr, "speed_min", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); uiItemR(col, ptr, "speed_max", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); - uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1636,8 +1656,8 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point { uiLayout *col; - uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, true); if (RNA_boolean_get(ptr, "relative")) { @@ -1660,8 +1680,8 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C) col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, ptr, "factor", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(col, ptr, "factor", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_double_edge_mask(uiLayout *layout, @@ -1683,7 +1703,7 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1691,17 +1711,17 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), uiLayout *sub, *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, nullptr, ICON_NONE); sub = uiLayoutColumn(col, false); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min")); uiItemR(sub, ptr, "min", DEFAULT_FLAGS, "", ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, nullptr, ICON_NONE); sub = uiLayoutColumn(col, false); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max")); uiItemR(sub, ptr, "max", DEFAULT_FLAGS, "", ICON_NONE); @@ -1712,8 +1732,8 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "premul", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "premul", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1721,27 +1741,27 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE); switch (RNA_enum_get(ptr, "mode")) { case CMP_NODE_DILATEERODE_DISTANCE_THRESH: - uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, nullptr, ICON_NONE); break; case CMP_NODE_DILATEERODE_DISTANCE_FEATHER: - uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, nullptr, ICON_NONE); break; } } static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1749,8 +1769,8 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1758,8 +1778,8 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_distance_matte(uiLayout *layout, @@ -1772,10 +1792,10 @@ static void node_composit_buts_distance_matte(uiLayout *layout, uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); - uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1784,23 +1804,23 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C) uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "limit_method") == 0) { uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } - uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_boolean_get(ptr, "use_unspill") == true) { - uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } } @@ -1809,13 +1829,13 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(layout, true); - /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now */ - uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now*/ + /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now */ + uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now*/ } static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1823,9 +1843,9 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C) uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_channel_matte(uiLayout *layout, @@ -1836,24 +1856,24 @@ static void node_composit_buts_channel_matte(uiLayout *layout, uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); col = uiLayoutColumn(layout, false); uiItemL(col, IFACE_("Key Channel:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "limit_method") == 0) { uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } - uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1861,19 +1881,19 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "index", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "index", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1905,7 +1925,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi /* disable stereo output for multilayer, too much work for something that no one will use */ /* if someone asks for that we can implement it */ if (is_multiview) { - uiTemplateImageFormatViews(layout, &imfptr, NULL); + uiTemplateImageFormatViews(layout, &imfptr, nullptr); } uiItemS(layout); @@ -1926,7 +1946,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi "layer_slots", ptr, "active_input_index", - NULL, + nullptr, 0, 0, 0, @@ -1944,7 +1964,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi "file_slots", ptr, "active_input_index", - NULL, + nullptr, 0, 0, 0, @@ -1959,9 +1979,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi col = uiLayoutColumn(row, true); wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false); - uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); RNA_enum_set(&op_ptr, "direction", 1); - uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); RNA_enum_set(&op_ptr, "direction", 2); if (active_input_ptr.data) { @@ -1975,10 +1995,10 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi "NODE_OT_output_file_remove_active_socket", "", ICON_X, - NULL, + nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY, - NULL); + nullptr); } else { col = uiLayoutColumn(layout, true); @@ -1990,23 +2010,23 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi "NODE_OT_output_file_remove_active_socket", "", ICON_X, - NULL, + nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY, - NULL); + nullptr); /* format details for individual files */ imfptr = RNA_pointer_get(&active_input_ptr, "format"); col = uiLayoutColumn(layout, true); uiItemL(col, IFACE_("Format:"), ICON_NONE); - uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, nullptr, ICON_NONE); const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format"); if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) { - uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, nullptr, ICON_NONE); } col = uiLayoutColumn(layout, false); @@ -2014,7 +2034,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi uiTemplateImageSettings(col, &imfptr, false); if (is_multiview) { - uiTemplateImageFormatViews(layout, &imfptr, NULL); + uiTemplateImageFormatViews(layout, &imfptr, nullptr); } } } @@ -2026,7 +2046,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { uiLayout *row; - uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(layout, true); uiItemR(row, ptr, "offset_x", DEFAULT_FLAGS, "X", ICON_NONE); uiItemR(row, ptr, "offset_y", DEFAULT_FLAGS, "Y", ICON_NONE); @@ -2043,8 +2063,8 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2054,86 +2074,86 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *split, *col, *row; - uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "correction_method") == 0) { split = uiLayoutSplit(layout, 0.0f, false); col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "lift", 1, 1, 0, 1); + uiTemplateColorPicker(col, ptr, "lift", true, true, false, true); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "gamma", 1, 1, 1, 1); + uiTemplateColorPicker(col, ptr, "gamma", true, true, true, true); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "gain", 1, 1, 1, 1); + uiTemplateColorPicker(col, ptr, "gain", true, true, true, true); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE); } else { split = uiLayoutSplit(layout, 0.0f, false); col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "offset", 1, 1, 0, 1); + uiTemplateColorPicker(col, ptr, "offset", true, true, false, true); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "power", 1, 1, 0, 1); + uiTemplateColorPicker(col, ptr, "power", true, true, false, true); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE); col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "slope", 1, 1, 0, 1); + uiTemplateColorPicker(col, ptr, "slope", true, true, false, true); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE); } } static void node_composit_buts_colorbalance_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "correction_method") == 0) { - uiTemplateColorPicker(layout, ptr, "lift", 1, 1, 0, 1); - uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true); + uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiTemplateColorPicker(layout, ptr, "gamma", 1, 1, 1, 1); - uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateColorPicker(layout, ptr, "gamma", true, true, true, true); + uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiTemplateColorPicker(layout, ptr, "gain", 1, 1, 1, 1); - uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateColorPicker(layout, ptr, "gain", true, true, true, true); + uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE); } else { - uiTemplateColorPicker(layout, ptr, "offset", 1, 1, 0, 1); - uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateColorPicker(layout, ptr, "offset", true, true, false, true); + uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiTemplateColorPicker(layout, ptr, "power", 1, 1, 0, 1); - uiItemR(layout, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateColorPicker(layout, ptr, "power", true, true, false, true); + uiItemR(layout, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE); - uiTemplateColorPicker(layout, ptr, "slope", 1, 1, 0, 1); - uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateColorPicker(layout, ptr, "slope", true, true, false, true); + uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE); } } static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - bNode *node = ptr->data; - CurveMapping *cumap = node->storage; + bNode *node = (bNode *)ptr->data; + CurveMapping *cumap = (CurveMapping *)node->storage; if (_sample_col[0] != SAMPLE_FLT_ISNONE) { cumap->flag |= CUMA_DRAW_SAMPLE; @@ -2153,17 +2173,33 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); } static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; PointerRNA clipptr; - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (!node->id) { return; @@ -2176,23 +2212,31 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (!node->id) { return; } uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2202,10 +2246,18 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (!node->id) { return; @@ -2221,9 +2273,9 @@ static void node_composit_buts_colorcorrection(uiLayout *layout, uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE); row = uiLayoutRow(layout, false); uiItemL(row, "", ICON_NONE); @@ -2266,8 +2318,8 @@ static void node_composit_buts_colorcorrection(uiLayout *layout, uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_colorcorrection_ex(uiLayout *layout, @@ -2277,53 +2329,53 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout, uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE); row = layout; uiItemL(row, IFACE_("Saturation"), ICON_NONE); - uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); uiItemL(row, IFACE_("Contrast"), ICON_NONE); - uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); uiItemL(row, IFACE_("Gamma"), ICON_NONE); - uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); uiItemL(row, IFACE_("Gain"), ICON_NONE); - uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); uiItemL(row, IFACE_("Lift"), ICON_NONE); - uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "check", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "check", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_switch_view_ex(uiLayout *layout, @@ -2334,10 +2386,10 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout, "NODE_OT_switch_view_update", "Update Views", ICON_FILE_REFRESH, - NULL, + nullptr, WM_OP_INVOKE_DEFAULT, 0, - NULL); + nullptr); } static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2345,32 +2397,32 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po uiLayout *row; row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); - uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE); - // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); /* UNUSED */ - uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE); /* UNUSED */ + uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_backdrop_viewer( @@ -2405,7 +2457,7 @@ static void node_composit_backdrop_viewer( static void node_composit_backdrop_boxmask( SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) { - NodeBoxMask *boxmask = node->storage; + NodeBoxMask *boxmask = (NodeBoxMask *)node->storage; const float backdropWidth = backdrop->x; const float backdropHeight = backdrop->y; const float aspect = backdropWidth / backdropHeight; @@ -2450,7 +2502,7 @@ static void node_composit_backdrop_boxmask( static void node_composit_backdrop_ellipsemask( SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) { - NodeEllipseMask *ellipsemask = node->storage; + NodeEllipseMask *ellipsemask = (NodeEllipseMask *)node->storage; const float backdropWidth = backdrop->x; const float backdropHeight = backdrop->y; const float aspect = backdropWidth / backdropHeight; @@ -2496,65 +2548,83 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C) { uiLayout *row; row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); - uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col; - uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "tile_order") == 0) { col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, nullptr, ICON_NONE); } } static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; - uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE); + uiTemplateID(layout, + C, + ptr, + "mask", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, nullptr, ICON_NONE); uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE); if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) { - uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, nullptr, ICON_NONE); } - uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE); if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) { - uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE); } } static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (node->id) { MovieClip *clip = (MovieClip *)node->id; @@ -2570,28 +2640,36 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - /* bNode *node = ptr->data; */ /* UNUSED */ + /* bNode *node = (bNode*)ptr->data; */ /* UNUSED */ - uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (node->id) { MovieClip *clip = (MovieClip *)node->id; @@ -2599,7 +2677,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN MovieTrackingObject *object; uiLayout *col; PointerRNA tracking_ptr; - NodeTrackPosData *data = node->storage; + NodeTrackPosData *data = (NodeTrackPosData *)node->storage; RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); @@ -2618,21 +2696,29 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN uiItemR(layout, ptr, "track_name", DEFAULT_FLAGS, "", ICON_ANIM_DATA); } - uiItemR(layout, ptr, "position", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "position", DEFAULT_FLAGS, nullptr, ICON_NONE); if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) { - uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, nullptr, ICON_NONE); } } } static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; - NodePlaneTrackDeformData *data = node->storage; + bNode *node = (bNode *)ptr->data; + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)node->storage; - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); if (node->id) { MovieClip *clip = (MovieClip *)node->id; @@ -2660,10 +2746,10 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P } } - uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE); if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE); } } @@ -2676,7 +2762,7 @@ static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, "", ICON_NONE); - uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } static void node_composit_buts_cryptomatte_legacy(uiLayout *layout, @@ -2704,18 +2790,35 @@ static void node_composit_buts_cryptomatte_legacy_ex(uiLayout *layout, static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; uiLayout *row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiLayout *col = uiLayoutColumn(layout, false); if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - uiTemplateID(col, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(col, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); } else { - uiTemplateID( - col, C, ptr, "image", NULL, "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); + uiTemplateID(col, + C, + ptr, + "image", + nullptr, + "IMAGE_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); NodeCryptomatte *crypto = (NodeCryptomatte *)node->storage; PointerRNA imaptr = RNA_pointer_get(ptr, "image"); @@ -2741,7 +2844,7 @@ static void node_composit_buts_brightcontrast(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE); } static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2751,14 +2854,13 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po #else /* Always supported through Accelerate framework BNNS on macOS. */ # ifndef __APPLE__ - if (!BLI_cpu_support_sse41()) -# endif - { + if (!BLI_cpu_support_sse41()) { uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); } +# endif #endif - uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, nullptr, ICON_NONE); } /* only once called */ @@ -3028,7 +3130,7 @@ static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), Poin static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { PointerRNA tex_ptr; - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; ID *id = ptr->owner_id; Tex *tex = (Tex *)node->storage; uiLayout *col, *row; @@ -3041,29 +3143,31 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe case TEX_BLEND: uiItemR(col, &tex_ptr, "progression", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR( + row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); break; case TEX_MARBLE: row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(col, false); uiItemR(row, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR( + row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); break; case TEX_MAGIC: - uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, nullptr, ICON_NONE); break; case TEX_STUCCI: row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); break; @@ -3071,18 +3175,19 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(col, &tex_ptr, "wood_type", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR( + row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(col, false); uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING))); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); break; case TEX_CLOUDS: uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(col, &tex_ptr, "noise_depth", @@ -3103,7 +3208,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe case TEX_VORONOI: uiItemR(col, &tex_ptr, "distance_metric", DEFAULT_FLAGS, "", ICON_NONE); if (tex->vn_distm == TEX_MINKOVSKY) { - uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, nullptr, ICON_NONE); } uiItemR(col, &tex_ptr, "color_mode", DEFAULT_FLAGS, "", ICON_NONE); break; @@ -3118,19 +3223,19 @@ static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *p "image", "IMAGE_OT_new", "IMAGE_OT_open", - NULL, + nullptr, UI_TEMPLATE_ID_FILTER_ALL, false, - NULL); + nullptr); } static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; PointerRNA iuserptr; RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false); } static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -3187,12 +3292,16 @@ static void node_texture_set_butfunc(bNodeType *ntype) } } -/* ****** init draw callbacks for all tree types, only called in usiblender.c, once ************ */ +/* -------------------------------------------------------------------- */ +/** \name Init Draw Callbacks For All Tree Types + * + * Only called on node initialization, once. + * \{ */ static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; ED_node_tag_update_nodetree(bmain, ntree, node); } @@ -3202,7 +3311,7 @@ static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocket PropertyRNA *prop = RNA_struct_type_find_property(srna, stemp->identifier); if (prop) { - RNA_def_property_update_runtime(prop, node_property_update_default); + RNA_def_property_update_runtime(prop, (const void *)node_property_update_default); } } @@ -3259,6 +3368,8 @@ static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C), r_color[3] = 1.0f; } +/** \} */ + void ED_node_init_butfuncs(void) { /* Fallback types for undefined tree, nodes, sockets @@ -3270,8 +3381,8 @@ void ED_node_init_butfuncs(void) NodeTypeUndefined.draw_nodetype_prepare = node_update_default; NodeTypeUndefined.select_area_func = node_select_area_default; NodeTypeUndefined.tweak_area_func = node_tweak_area_default; - NodeTypeUndefined.draw_buttons = NULL; - NodeTypeUndefined.draw_buttons_ex = NULL; + NodeTypeUndefined.draw_buttons = nullptr; + NodeTypeUndefined.draw_buttons_ex = nullptr; NodeTypeUndefined.resize_area_func = node_resize_area_default; NodeSocketTypeUndefined.draw = node_socket_undefined_draw; @@ -3348,7 +3459,7 @@ static void std_node_socket_draw_color(bContext *UNUSED(C), PointerRNA *UNUSED(node_ptr), float *r_color) { - bNodeSocket *sock = ptr->data; + bNodeSocket *sock = (bNodeSocket *)ptr->data; int type = sock->typeinfo->type; copy_v4_v4(r_color, std_node_socket_colors[type]); } @@ -3356,7 +3467,7 @@ static void std_node_socket_interface_draw_color(bContext *UNUSED(C), PointerRNA *ptr, float *r_color) { - bNodeSocket *sock = ptr->data; + bNodeSocket *sock = (bNodeSocket *)ptr->data; int type = sock->typeinfo->type; copy_v4_v4(r_color, std_node_socket_colors[type]); } @@ -3369,7 +3480,7 @@ static void node_file_output_socket_draw(bContext *C, PointerRNA *node_ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = ptr->data; + bNodeSocket *sock = (bNodeSocket *)ptr->data; uiLayout *row; PointerRNA inputptr; @@ -3379,13 +3490,13 @@ static void node_file_output_socket_draw(bContext *C, int imtype = RNA_enum_get(&imfptr, "file_format"); if (imtype == R_IMF_IMTYPE_MULTILAYER) { - NodeImageMultiFileSocket *input = sock->storage; + NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage; RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr); uiItemL(row, input->layer, ICON_NONE); } else { - NodeImageMultiFileSocket *input = sock->storage; + NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage; uiBlock *block; RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotFile, input, &inputptr); @@ -3412,8 +3523,8 @@ static void node_file_output_socket_draw(bContext *C, static void std_node_socket_draw( bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text) { - bNode *node = node_ptr->data; - bNodeSocket *sock = ptr->data; + bNode *node = (bNode *)node_ptr->data; + bNodeSocket *sock = (bNodeSocket *)ptr->data; int type = sock->typeinfo->type; /*int subtype = sock->typeinfo->subtype;*/ @@ -3489,7 +3600,8 @@ static void std_node_socket_draw( break; } case SOCK_TEXTURE: { - uiTemplateID(layout, C, ptr, "default_value", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); + uiTemplateID( + layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); break; } case SOCK_MATERIAL: { @@ -3504,7 +3616,7 @@ static void std_node_socket_draw( static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout, PointerRNA *ptr) { - bNodeSocket *sock = ptr->data; + bNodeSocket *sock = (bNodeSocket *)ptr->data; int type = sock->typeinfo->type; uiLayout *col = uiLayoutColumn(layout, false); @@ -3539,7 +3651,7 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout } } - uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0); } void ED_init_standard_node_socket_type(bNodeSocketType *stype) @@ -3606,7 +3718,7 @@ void draw_nodespace_back_pix(const bContext *C, void *lock; Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (ibuf) { /* somehow the offset has to be calculated inverse */ wmOrtho2_region_pixelspace(region); @@ -3615,7 +3727,7 @@ void draw_nodespace_back_pix(const bContext *C, /** \note draw selected info on backdrop */ if (snode->edittree) { - bNode *node = snode->edittree->nodes.first; + bNode *node = (bNode *)snode->edittree->nodes.first; rctf *viewer_border = &snode->nodetree->viewer_border; while (node) { if (node->flag & NODE_SELECT) { @@ -3652,7 +3764,7 @@ void draw_nodespace_back_pix(const bContext *C, GPU_matrix_pop(); } -/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */ +/* return quadratic beziers points for a given nodelink and clip if v2d is not nullptr. */ bool node_link_bezier_handles(const View2D *v2d, const SpaceNode *snode, const bNodeLink *link, @@ -3682,7 +3794,7 @@ bool node_link_bezier_handles(const View2D *v2d, fromreroute = (link->fromnode && link->fromnode->type == NODE_REROUTE); } else { - if (snode == NULL) { + if (snode == nullptr) { return false; } copy_v2_v2(vec[0], cursor); @@ -3701,7 +3813,7 @@ bool node_link_bezier_handles(const View2D *v2d, toreroute = (link->tonode && link->tonode->type == NODE_REROUTE); } else { - if (snode == NULL) { + if (snode == nullptr) { return false; } copy_v2_v2(vec[3], cursor); @@ -3761,7 +3873,7 @@ bool node_link_bezier_handles(const View2D *v2d, return true; } -/* if v2d not NULL, it clips and returns 0 if not visible */ +/* if v2d not nullptr, it clips and returns 0 if not visible */ bool node_link_bezier_points(const View2D *v2d, const SpaceNode *snode, const bNodeLink *link, @@ -3794,6 +3906,7 @@ static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0. static float mute_verts[3][2] = {{0.7071f, 1.0f}, {0.7071f, 0.0f}, {0.7071f, -1.0f}}; static float mute_expand_axis[3][2] = {{1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, -0.0f}}; +/* Is zero initialized because it is static data. */ static struct { GPUBatch *batch; /* for batching line together */ GPUBatch *batch_single; /* for single line */ @@ -3804,9 +3917,9 @@ static struct { GPUVertBufRaw colid_step, muted_step; uint count; bool enabled; -} g_batch_link = {0}; +} g_batch_link; -static void nodelink_batch_reset(void) +static void nodelink_batch_reset() { GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p0_id, &g_batch_link.p0_step); GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p1_id, &g_batch_link.p1_step); @@ -3833,7 +3946,7 @@ static void set_nodelink_vertex(GPUVertBuf *vbo, GPU_vertbuf_attr_set(vbo, exp_id, v, exp); } -static void nodelink_batch_init(void) +static void nodelink_batch_init() { GPUVertFormat format = {0}; uint uv_id = GPU_vertformat_attr_add(&format, "uv", GPU_COMP_U8, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); @@ -3912,10 +4025,11 @@ static void nodelink_batch_init(void) } } - g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); + g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO); gpu_batch_presets_register(g_batch_link.batch); - g_batch_link.batch_single = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, 0); + g_batch_link.batch_single = GPU_batch_create_ex( + GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_INVALID); gpu_batch_presets_register(g_batch_link.batch_single); /* Instances data */ @@ -4015,16 +4129,16 @@ static void nodelink_batch_add_link(const SpaceNode *snode, BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1)); g_batch_link.count++; - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0); - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1); - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2); - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3); - char *colid = GPU_vertbuf_raw_step(&g_batch_link.colid_step); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3); + char *colid = (char *)GPU_vertbuf_raw_step(&g_batch_link.colid_step); colid[0] = nodelink_get_color_id(th_col1); colid[1] = nodelink_get_color_id(th_col2); colid[2] = nodelink_get_color_id(th_col3); colid[3] = drawarrow; - char *muted = GPU_vertbuf_raw_step(&g_batch_link.muted_step); + char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); muted[0] = drawmuted; if (g_batch_link.count == NODELINK_GROUP_SIZE) { @@ -4046,7 +4160,7 @@ void node_draw_link_bezier(const View2D *v2d, int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) && (link->fromnode && (link->fromnode->type == NODE_REROUTE))); int drawmuted = (link->flag & NODE_LINK_MUTED); - if (g_batch_link.batch == NULL) { + if (g_batch_link.batch == nullptr) { nodelink_batch_init(); } @@ -4088,7 +4202,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) { int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; - if (link->fromsock == NULL && link->tosock == NULL) { + if (link->fromsock == nullptr && link->tosock == nullptr) { return; } diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.cc index a28d467df2e..7276688e986 100644 --- a/source/blender/editors/space_node/node_add.c +++ b/source/blender/editors/space_node/node_add.cc @@ -65,13 +65,13 @@ /** * XXX Does some additional initialization on top of #nodeAddNode * Can be used with both custom and static nodes, - * if `idname == NULL` the static int type will be used instead. + * if `idname == nullptr` the static int type will be used instead. */ bNode *node_add_node(const bContext *C, const char *idname, int type, float locx, float locy) { SpaceNode *snode = CTX_wm_space_node(C); Main *bmain = CTX_data_main(C); - bNode *node = NULL; + bNode *node = nullptr; node_deselect_all(snode); @@ -90,7 +90,7 @@ bNode *node_add_node(const bContext *C, const char *idname, int type, float locx nodeSetSelected(node, true); ntreeUpdateTree(bmain, snode->edittree); - ED_node_set_active(bmain, snode->edittree, node, NULL); + ED_node_set_active(bmain, snode->edittree, node, nullptr); snode_update(snode, node); @@ -114,7 +114,7 @@ static bool add_reroute_intersect_check(bNodeLink *link, { float coord_array[NODE_LINK_RESOL + 1][2]; - if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) { + if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { for (int i = 0; i < tot - 1; i++) { for (int b = 0; b < NODE_LINK_RESOL; b++) { if (isect_seg_seg_v2_point( @@ -127,13 +127,13 @@ static bool add_reroute_intersect_check(bNodeLink *link, return false; } -typedef struct bNodeSocketLink { +struct bNodeSocketLink { struct bNodeSocketLink *next, *prev; struct bNodeSocket *sock; struct bNodeLink *link; float point[2]; -} bNodeSocketLink; +}; static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb, bNodeSocket *sock, @@ -142,12 +142,12 @@ static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb, { bNodeSocketLink *socklink, *prev; - socklink = MEM_callocN(sizeof(bNodeSocketLink), "socket link"); + socklink = (bNodeSocketLink *)MEM_callocN(sizeof(bNodeSocketLink), "socket link"); socklink->sock = sock; socklink->link = link; copy_v2_v2(socklink->point, point); - for (prev = lb->last; prev; prev = prev->prev) { + for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) { if (prev->sock == sock) { break; } @@ -162,7 +162,7 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C, { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNode *reroute_node = NULL; + bNode *reroute_node = nullptr; bNodeSocket *cursock = socklink->sock; float insert_point[2]; int num_links; @@ -184,12 +184,12 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C, socklink->link->fromnode, socklink->link->fromsock, reroute_node, - reroute_node->inputs.first); + (bNodeSocket *)reroute_node->inputs.first); } else { nodeAddLink(ntree, reroute_node, - reroute_node->outputs.first, + (bNodeSocket *)reroute_node->outputs.first, socklink->link->tonode, socklink->link->tosock); } @@ -198,11 +198,11 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C, /* insert the reroute node into the link */ if (in_out == SOCK_OUT) { socklink->link->fromnode = reroute_node; - socklink->link->fromsock = reroute_node->outputs.first; + socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first; } else { socklink->link->tonode = reroute_node; - socklink->link->tosock = reroute_node->inputs.first; + socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first; } add_v2_v2(insert_point, socklink->point); @@ -259,7 +259,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) BLI_listbase_clear(&output_links); BLI_listbase_clear(&input_links); - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (nodeLinkIsHidden(link)) { continue; } @@ -275,11 +275,11 @@ static int add_reroute_exec(bContext *C, wmOperator *op) /* Create reroute nodes for intersected links. * Only one reroute if links share the same input/output socket. */ - socklink = output_links.first; + socklink = (bNodeSocketLink *)output_links.first; while (socklink) { socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT); } - socklink = input_links.first; + socklink = (bNodeSocketLink *)input_links.first; while (socklink) { socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN); } @@ -317,7 +317,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot) /* properties */ PropertyRNA *prop; prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); /* internal */ RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } @@ -337,10 +337,10 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name); if (!node_group) { - return NULL; + return nullptr; } - const char *disabled_hint = NULL; + const char *disabled_hint = nullptr; if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) { if (disabled_hint) { BKE_reportf(op->reports, @@ -358,7 +358,7 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, ntree->id.name + 2); } - return NULL; + return nullptr; } return node_group; @@ -450,7 +450,7 @@ static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOper Object *object = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); if (!object) { - return NULL; + return nullptr; } return object; @@ -470,7 +470,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); bNode *object_node = node_add_node( - C, NULL, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); + C, nullptr, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!object_node) { BKE_report(op->reports, RPT_WARNING, "Could not add node object"); return OPERATOR_CANCELLED; @@ -482,7 +482,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bNodeSocketValueObject *socket_data = sock->default_value; + bNodeSocketValueObject *socket_data = (bNodeSocketValueObject *)sock->default_value; socket_data->value = object; id_us_plus(&object->id); @@ -554,7 +554,7 @@ static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOpera Tex *texture = (Tex *)BKE_libblock_find_name(bmain, ID_TE, name); if (!texture) { - return NULL; + return nullptr; } return texture; @@ -574,7 +574,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); bNode *texture_node = node_add_node(C, - NULL, + nullptr, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, snode->runtime->cursor[0], snode->runtime->cursor[1]); @@ -655,7 +655,7 @@ static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *b Collection *collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); if (!collection) { - return NULL; + return nullptr; } return collection; @@ -675,7 +675,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); bNode *collection_node = node_add_node( - C, NULL, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); + C, nullptr, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!collection_node) { BKE_report(op->reports, RPT_WARNING, "Could not add node collection"); return OPERATOR_CANCELLED; @@ -687,7 +687,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bNodeSocketValueCollection *socket_data = sock->default_value; + bNodeSocketValueCollection *socket_data = (bNodeSocketValueCollection *)sock->default_value; socket_data->value = collection; id_us_plus(&collection->id); @@ -788,7 +788,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - node = node_add_node(C, NULL, type, snode->runtime->cursor[0], snode->runtime->cursor[1]); + node = node_add_node(C, nullptr, type, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!node) { BKE_report(op->reports, RPT_WARNING, "Could not add an image node"); @@ -801,7 +801,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) * to get proper image source. */ if (RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD); + BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_RELOAD); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } @@ -876,7 +876,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNode *node; - ID *mask = NULL; + ID *mask = nullptr; /* check input variables */ char name[MAX_ID_NAME - 2]; @@ -890,7 +890,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); node = node_add_node( - C, NULL, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]); + C, nullptr, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!node) { BKE_report(op->reports, RPT_WARNING, "Could not add a mask node"); @@ -976,7 +976,7 @@ static int new_node_tree_exec(bContext *C, wmOperator *op) id_us_min(&ntree->id); RNA_id_pointer_create(&ntree->id, &idptr); - RNA_property_pointer_set(&ptr, prop, idptr, NULL); + RNA_property_pointer_set(&ptr, prop, idptr, nullptr); RNA_property_update(C, &ptr, prop); } else if (snode) { @@ -993,7 +993,7 @@ static const EnumPropertyItem *new_node_tree_type_itemf(bContext *UNUSED(C), PropertyRNA *UNUSED(prop), bool *r_free) { - return rna_node_tree_type_itemf(NULL, NULL, r_free); + return rna_node_tree_type_itemf(nullptr, nullptr, r_free); } void NODE_OT_new_node_tree(wmOperatorType *ot) diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.cc index 50fa8b28468..d86b069aac3 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.cc @@ -83,7 +83,7 @@ enum { COM_RECALC_VIEWER = 2, }; -typedef struct CompoJob { +struct CompoJob { /* Input parameters. */ Main *bmain; Scene *scene; @@ -97,7 +97,7 @@ typedef struct CompoJob { const short *stop; short *do_update; float *progress; -} CompoJob; +}; float node_socket_calculate_height(const bNodeSocket *socket) { @@ -150,7 +150,7 @@ static int compo_get_recalc_flags(const bContext *C) LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; + SpaceImage *sima = (SpaceImage *)area->spacedata.first; if (sima->image) { if (sima->image->type == IMA_TYPE_R_RESULT) { recalc_flags |= COM_RECALC_COMPOSITE; @@ -161,7 +161,7 @@ static int compo_get_recalc_flags(const bContext *C) } } else if (area->spacetype == SPACE_NODE) { - SpaceNode *snode = area->spacedata.first; + SpaceNode *snode = (SpaceNode *)area->spacedata.first; if (snode->flag & SNODE_BACKDRAW) { recalc_flags |= COM_RECALC_VIEWER; } @@ -175,7 +175,7 @@ static int compo_get_recalc_flags(const bContext *C) /* called by compo, only to check job 'stop' value */ static int compo_breakjob(void *cjv) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; /* without G.is_break 'ESC' wont quit - which annoys users */ return (*(cj->stop) @@ -188,7 +188,7 @@ static int compo_breakjob(void *cjv) /* called by compo, wmJob sends notifier */ static void compo_statsdrawjob(void *cjv, const char *UNUSED(str)) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; *(cj->do_update) = true; } @@ -196,19 +196,19 @@ static void compo_statsdrawjob(void *cjv, const char *UNUSED(str)) /* called by compo, wmJob sends notifier */ static void compo_redrawjob(void *cjv) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; *(cj->do_update) = true; } static void compo_freejob(void *cjv) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; if (cj->localtree) { ntreeLocalMerge(cj->bmain, cj->localtree, cj->ntree); } - if (cj->compositor_depsgraph != NULL) { + if (cj->compositor_depsgraph != nullptr) { DEG_graph_free(cj->compositor_depsgraph); } MEM_freeN(cj); @@ -218,7 +218,7 @@ static void compo_freejob(void *cjv) * sliding buttons doesn't frustrate */ static void compo_initjob(void *cjv) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; Main *bmain = cj->bmain; Scene *scene = cj->scene; ViewLayer *view_layer = cj->view_layer; @@ -243,12 +243,12 @@ static void compo_initjob(void *cjv) /* called before redraw notifiers, it moves finished previews over */ static void compo_updatejob(void *UNUSED(cjv)) { - WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, NULL); + WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, nullptr); } static void compo_progressjob(void *cjv, float progress) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; *(cj->progress) = progress; } @@ -261,7 +261,7 @@ static void compo_startjob(void *cjv, short *do_update, float *progress) { - CompoJob *cj = cjv; + CompoJob *cj = (CompoJob *)cjv; bNodeTree *ntree = cj->localtree; Scene *scene = cj->scene; @@ -311,9 +311,9 @@ static void compo_startjob(void *cjv, } } - ntree->test_break = NULL; - ntree->stats_draw = NULL; - ntree->progress = NULL; + ntree->test_break = nullptr; + ntree->stats_draw = nullptr; + ntree->progress = nullptr; } /** @@ -347,7 +347,7 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene "Compositing", WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS, WM_JOB_TYPE_COMPOSITE); - CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job"); + CompoJob *cj = (CompoJob *)MEM_callocN(sizeof(CompoJob), "compo job"); /* customdata for preview thread */ cj->bmain = bmain; @@ -359,7 +359,7 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene /* setup job */ WM_jobs_customdata_set(wm_job, cj, compo_freejob); WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT); - WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, NULL); + WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, nullptr); WM_jobs_start(CTX_wm_manager(C), wm_job); } @@ -412,7 +412,7 @@ void snode_notify(bContext *C, SpaceNode *snode) { ID *id = snode->id; - WM_event_add_notifier(C, NC_NODE | NA_EDITED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_EDITED, nullptr); if (ED_node_is_shader(snode)) { if (GS(id->name) == ID_MA) { @@ -490,15 +490,15 @@ void ED_node_shader_default(const bContext *C, ID *id) } else if (ELEM(GS(id->name), ID_WO, ID_LA)) { /* Emission */ - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); + bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); bNode *shader, *output; if (GS(id->name) == ID_WO) { World *world = (World *)id; world->nodetree = ntree; - shader = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND); - output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD); + shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND); + output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD); nodeAddLink(ntree, shader, nodeFindSocket(shader, SOCK_OUT, "Background"), @@ -512,8 +512,8 @@ void ED_node_shader_default(const bContext *C, ID *id) Light *light = (Light *)id; light->nodetree = ntree; - shader = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); - output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_LIGHT); + shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION); + output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_LIGHT); nodeAddLink(ntree, shader, nodeFindSocket(shader, SOCK_OUT, "Emission"), @@ -546,7 +546,7 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce) return; } - sce->nodetree = ntreeAddTree(NULL, "Compositing Nodetree", ntreeType_Composite->idname); + sce->nodetree = ntreeAddTree(nullptr, "Compositing Nodetree", ntreeType_Composite->idname); sce->nodetree->chunksize = 256; sce->nodetree->edit_quality = NTREE_QUALITY_HIGH; @@ -562,8 +562,8 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce) nodeSetActive(sce->nodetree, in); /* links from color to color */ - bNodeSocket *fromsock = in->outputs.first; - bNodeSocket *tosock = out->inputs.first; + bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first; + bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(sce->nodetree, in, fromsock, out, tosock); ntreeUpdateTree(CTX_data_main(C), sce->nodetree); @@ -581,7 +581,7 @@ void ED_node_texture_default(const bContext *C, Tex *tex) return; } - tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname); + tex->nodetree = ntreeAddTree(nullptr, "Texture Nodetree", ntreeType_Texture->idname); bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT); out->locx = 300.0f; @@ -592,8 +592,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex) in->locy = 300.0f; nodeSetActive(tex->nodetree, in); - bNodeSocket *fromsock = in->outputs.first; - bNodeSocket *tosock = out->inputs.first; + bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first; + bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; nodeAddLink(tex->nodetree, in, fromsock, out, tosock); ntreeUpdateTree(CTX_data_main(C), tex->nodetree); @@ -618,24 +618,24 @@ void snode_set_context(const bContext *C) if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) { /* current tree does not match selected type, clear tree path */ - ntree = NULL; - id = NULL; - from = NULL; + ntree = nullptr; + id = nullptr; + from = nullptr; } - if (!(snode->flag & SNODE_PIN) || ntree == NULL) { + if (!(snode->flag & SNODE_PIN) || ntree == nullptr) { if (treetype->get_from_context) { /* reset and update from context */ - ntree = NULL; - id = NULL; - from = NULL; + ntree = nullptr; + id = nullptr; + from = nullptr; treetype->get_from_context(C, treetype, &ntree, &id, &from); } } if (snode->nodetree != ntree || snode->id != id || snode->from != from || - (snode->treepath.last == NULL && ntree)) { + (snode->treepath.last == nullptr && ntree)) { ED_node_tree_start(snode, ntree, id, from); } } @@ -648,7 +648,7 @@ void snode_update(SpaceNode *snode, bNode *node) */ /* update all edited group nodes */ - bNodeTreePath *path = snode->treepath.last; + bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; if (path) { bNodeTree *ngroup = path->nodetree; for (path = path->prev; path; path = path->prev) { @@ -685,7 +685,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti node->flag |= NODE_DO_OUTPUT; if (!was_output) { - do_update = 1; + do_update = true; } } @@ -734,7 +734,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti *r_active_texture_changed = true; } ED_node_tag_update_nodetree(bmain, ntree, node); - WM_main_add_notifier(NC_IMAGE, NULL); + WM_main_add_notifier(NC_IMAGE, nullptr); } WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id); @@ -753,7 +753,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti ED_node_tag_update_nodetree(bmain, ntree, node); } - /* addnode() doesn't link this yet... */ + /* Adding a node doesn't link this yet. */ node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); } else if (node->type == CMP_NODE_COMPOSITE) { @@ -806,7 +806,7 @@ static bool edit_node_poll(bContext *C) static void edit_node_properties(wmOperatorType *ot) { /* XXX could node be a context pointer? */ - RNA_def_string(ot->srna, "node", NULL, MAX_NAME, "Node", ""); + RNA_def_string(ot->srna, "node", nullptr, MAX_NAME, "Node", ""); RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET); RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", ""); } @@ -838,7 +838,7 @@ static void edit_node_properties_get( wmOperator *op, bNodeTree *ntree, bNode **r_node, bNodeSocket **r_sock, int *r_in_out) { bNode *node; - bNodeSocket *sock = NULL; + bNodeSocket *sock = nullptr; char nodename[MAX_NAME]; int sockindex; int in_out; @@ -876,29 +876,30 @@ static void edit_node_properties_get( static bNode *visible_node(SpaceNode *snode, const rctf *rct) { LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode->edittree->nodes) { - if (BLI_rctf_isect(&node->totr, rct, NULL)) { + if (BLI_rctf_isect(&node->totr, rct, nullptr)) { return node; } } - return NULL; + return nullptr; } /* ********************** size widget operator ******************** */ -typedef struct NodeSizeWidget { +struct NodeSizeWidget { float mxstart, mystart; float oldlocx, oldlocy; float oldoffsetx, oldoffsety; float oldwidth, oldheight; int directions; -} NodeSizeWidget; +}; static void node_resize_init( bContext *C, wmOperator *op, const wmEvent *UNUSED(event), bNode *node, int dir) { SpaceNode *snode = CTX_wm_space_node(C); - NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data"); + NodeSizeWidget *nsw = (NodeSizeWidget *)MEM_callocN(sizeof(NodeSizeWidget), + "size widget op data"); op->customdata = nsw; nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC; @@ -926,7 +927,7 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel) if (cancel) { SpaceNode *snode = CTX_wm_space_node(C); bNode *node = nodeGetActive(snode->edittree); - NodeSizeWidget *nsw = op->customdata; + NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata; node->locx = nsw->oldlocx; node->locy = nsw->oldlocy; @@ -937,7 +938,7 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel) } MEM_freeN(op->customdata); - op->customdata = NULL; + op->customdata = nullptr; } static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) @@ -945,7 +946,7 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) SpaceNode *snode = CTX_wm_space_node(C); ARegion *region = CTX_wm_region(C); bNode *node = nodeGetActive(snode->edittree); - NodeSizeWidget *nsw = op->customdata; + NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata; switch (event->type) { case MOUSEMOVE: { @@ -1112,7 +1113,7 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) else { /* hide unused sockets */ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (sock->link == NULL) { + if (sock->link == nullptr) { sock->flag |= SOCK_HIDDEN; } } @@ -1128,17 +1129,17 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket) { const float node_socket_height = node_socket_calculate_height(socket); - const rctf multi_socket_rect = { - .xmin = socket->locx - NODE_SOCKSIZE * 4.0f, - .xmax = socket->locx + NODE_SOCKSIZE * 2.0f, - /*.xmax = socket->locx + NODE_SOCKSIZE * 5.5f - * would be the same behavior as for regular sockets. - * But keep it smaller because for multi-input socket you - * sometimes want to drag the link to the other side, if you may - * accidentally pick the wrong link otherwise. */ - .ymin = socket->locy - node_socket_height, - .ymax = socket->locy + node_socket_height, - }; + rctf multi_socket_rect; + /*.xmax = socket->locx + NODE_SOCKSIZE * 5.5f + * would be the same behavior as for regular sockets. + * But keep it smaller because for multi-input socket you + * sometimes want to drag the link to the other side, if you may + * accidentally pick the wrong link otherwise. */ + BLI_rctf_init(&multi_socket_rect, + socket->locx - NODE_SOCKSIZE * 4.0f, + socket->locx + NODE_SOCKSIZE * 2.0f, + socket->locy - node_socket_height, + socket->locy + node_socket_height); if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) { return true; } @@ -1151,8 +1152,8 @@ int node_find_indicated_socket( { rctf rect; - *nodep = NULL; - *sockp = NULL; + *nodep = nullptr; + *sockp = nullptr; /* check if we click in a socket */ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { @@ -1244,7 +1245,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - bNode *lastnode = ntree->nodes.last; + bNode *lastnode = (bNode *)ntree->nodes.last; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); @@ -1262,14 +1263,14 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) /* copy links between selected nodes * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! */ - bNodeLink *lastlink = ntree->links.last; + bNodeLink *lastlink = (bNodeLink *)ntree->links.last; LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { /* This creates new links between copied nodes. - * If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)! + * If keep_inputs is set, also copies input links from unselected (when fromnode==nullptr)! */ if (link->tonode && (link->tonode->flag & NODE_SELECT) && (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) { - bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink"); newlink->flag = link->flag; newlink->tonode = link->tonode->new_node; newlink->tosock = link->tosock->new_sock; @@ -1353,7 +1354,7 @@ void NODE_OT_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean( - ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes"); + ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes"); } bool ED_node_select_check(ListBase *lb) @@ -1414,7 +1415,7 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) if ((node->type == CMP_NODE_R_LAYERS) || (node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER)) { ID *id = node->id; - if (id == NULL) { + if (id == nullptr) { continue; } if (id->tag & LIB_TAG_DOIT) { @@ -1453,7 +1454,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) /* This is actually a test whether scene is used by the compositor or not. * All the nodes are using same render result, so there is no need to do * anything smart about check how exactly scene is used. */ - bNode *node = NULL; + bNode *node = nullptr; LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) { if (node_iter->id == (ID *)sce) { node = node_iter; @@ -1462,7 +1463,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) } if (node) { - ViewLayer *view_layer = BLI_findlink(&sce->view_layers, node->custom1); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, node->custom1); if (view_layer) { PointerRNA op_ptr; @@ -1471,7 +1472,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) RNA_string_set(&op_ptr, "layer", view_layer->name); RNA_string_set(&op_ptr, "scene", sce->id.name + 2); - /* to keep keypositions */ + /* To keep keyframe positions. */ sce->r.scemode |= R_NO_FRAME_UPDATE; WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr); @@ -1554,13 +1555,13 @@ static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op)) SpaceNode *snode = CTX_wm_space_node(C); /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { + if ((snode == nullptr) || (snode->edittree == nullptr)) { return OPERATOR_CANCELLED; } node_flag_toggle_exec(snode, NODE_HIDDEN); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1585,7 +1586,7 @@ static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op)) SpaceNode *snode = CTX_wm_space_node(C); /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { + if ((snode == nullptr) || (snode->edittree == nullptr)) { return OPERATOR_CANCELLED; } @@ -1618,13 +1619,13 @@ static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op)) SpaceNode *snode = CTX_wm_space_node(C); /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { + if ((snode == nullptr) || (snode->edittree == nullptr)) { return OPERATOR_CANCELLED; } node_flag_toggle_exec(snode, NODE_OPTIONS); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1649,7 +1650,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) SpaceNode *snode = CTX_wm_space_node(C); /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { + if ((snode == nullptr) || (snode->edittree == nullptr)) { return OPERATOR_CANCELLED; } @@ -1674,7 +1675,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) ntreeUpdateTree(CTX_data_main(C), snode->edittree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1870,12 +1871,12 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); SpaceNode *snode = CTX_wm_space_node(C); PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; + bNodeTree *ntree = nullptr; + bNode *node = nullptr; char file_path[MAX_NAME]; if (ptr.data) { - node = ptr.data; + node = (bNode *)ptr.data; ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { @@ -1919,11 +1920,11 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U { SpaceNode *snode = CTX_wm_space_node(C); PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; + bNodeTree *ntree = nullptr; + bNode *node = nullptr; if (ptr.data) { - node = ptr.data; + node = (bNode *)ptr.data; ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { @@ -1965,10 +1966,10 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNode *node = NULL; + bNode *node = nullptr; if (ptr.data) { - node = ptr.data; + node = (bNode *)ptr.data; } else if (snode && snode->edittree) { node = nodeGetActive(snode->edittree); @@ -1978,9 +1979,9 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - NodeImageMultiFile *nimf = node->storage; + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; - bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input); + bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input); if (!sock) { return OPERATOR_CANCELLED; } @@ -2014,7 +2015,7 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) { static const EnumPropertyItem direction_items[] = { - {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, NULL, 0, NULL, NULL}}; + {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, nullptr, 0, nullptr, nullptr}}; /* identifiers */ ot->name = "Move File Node Socket"; @@ -2059,7 +2060,7 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) } ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -2097,7 +2098,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) /* No ID refcounting, this node is virtual, * detached from any actual Blender data currently. */ bNode *new_node = BKE_node_copy_store_new_pointers( - NULL, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN); + nullptr, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN); BKE_node_clipboard_add_node(new_node); } } @@ -2127,7 +2128,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) /* This creates new links between copied nodes. */ if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode && (link->fromnode->flag & NODE_SELECT)) { - bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink"); newlink->flag = link->flag; newlink->tonode = link->tonode->new_node; newlink->tosock = link->tosock->new_sock; @@ -2188,7 +2189,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) /* make sure all clipboard nodes would be valid in the target tree */ bool all_nodes_valid = true; LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { - const char *disabled_hint = NULL; + const char *disabled_hint = nullptr; if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree, &disabled_hint)) { all_nodes_valid = false; @@ -2286,7 +2287,7 @@ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) return socket; } } - return NULL; + return nullptr; } static int ntree_socket_add_exec(bContext *C, wmOperator *op) @@ -2297,7 +2298,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) PointerRNA ntree_ptr; RNA_id_pointer_create((ID *)ntree, &ntree_ptr); - const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out"); + const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; const char *default_name = (in_out == SOCK_IN) ? "Input" : "Output"; @@ -2328,7 +2329,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -2356,11 +2357,11 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out"); + const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs); - if (iosock == NULL) { + if (iosock == nullptr) { return OPERATOR_CANCELLED; } @@ -2378,7 +2379,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -2404,7 +2405,7 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot) static const EnumPropertyItem move_direction_items[] = { {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static int ntree_socket_move_exec(bContext *C, wmOperator *op) @@ -2413,12 +2414,12 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op) bNodeTree *ntree = snode->edittree; int direction = RNA_enum_get(op->ptr, "direction"); - const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out"); + const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs; bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); - if (iosock == NULL) { + if (iosock == nullptr) { return OPERATOR_CANCELLED; } @@ -2453,7 +2454,7 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op) snode_notify(C, snode); snode_dag_update(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -2486,18 +2487,18 @@ static bool node_shader_script_update_poll(bContext *C) /* test if we have a render engine that supports shaders scripts */ if (!(type && type->update_script_node)) { - return 0; + return false; } /* see if we have a shader script node in context */ - bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data; + bNode *node = (bNode *)CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data; if (!node && snode && snode->edittree) { node = nodeGetActive(snode->edittree); } if (node && node->type == SH_NODE_SCRIPT) { - NodeShaderScript *nss = node->storage; + NodeShaderScript *nss = (NodeShaderScript *)node->storage; if (node->id || nss->filepath[0]) { return ED_operator_node_editable(C); @@ -2505,14 +2506,14 @@ static bool node_shader_script_update_poll(bContext *C) } /* see if we have a text datablock in context */ - Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; + Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; if (text) { - return 1; + return true; } /* we don't check if text datablock is actually in use, too slow for poll */ - return 0; + return false; } /* recursively check for script nodes in groups using this text and update */ @@ -2556,11 +2557,11 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op) engine->reports = op->reports; /* get node */ - bNodeTree *ntree_base = NULL; - bNode *node = NULL; + bNodeTree *ntree_base = nullptr; + bNode *node = nullptr; if (nodeptr.data) { ntree_base = (bNodeTree *)nodeptr.owner_id; - node = nodeptr.data; + node = (bNode *)nodeptr.data; } else if (snode && snode->edittree) { ntree_base = snode->edittree; @@ -2575,7 +2576,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op) } else { /* update all nodes using text datablock */ - Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; + Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; if (text) { /* clear flags for recursion check */ @@ -2647,7 +2648,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), bmain); Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (ibuf) { ARegion *region = CTX_wm_region(C); @@ -2683,7 +2684,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op) } snode_notify(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); } else { btree->flag &= ~NTREE_VIEWER_BORDER; @@ -2723,7 +2724,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) btree->flag &= ~NTREE_VIEWER_BORDER; snode_notify(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -2749,11 +2750,11 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; + bNodeTree *ntree = nullptr; + bNode *node = nullptr; if (ptr.data) { - node = ptr.data; + node = (bNode *)ptr.data; ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { @@ -2793,11 +2794,11 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o { SpaceNode *snode = CTX_wm_space_node(C); PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; + bNodeTree *ntree = nullptr; + bNode *node = nullptr; if (ptr.data) { - node = ptr.data; + node = (bNode *)ptr.data; ntree = (bNodeTree *)ptr.owner_id; } else if (snode && snode->edittree) { diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.cc index 335e2f93ff3..34450016698 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.cc @@ -21,7 +21,7 @@ * \ingroup spnode */ -#include <stdlib.h> +#include <cstdlib> #include "MEM_guardedalloc.h" @@ -135,7 +135,7 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname) if (node && STREQ(node->idname, node_idname)) { return node; } - return NULL; + return nullptr; } /** \} */ @@ -165,7 +165,7 @@ static int node_group_edit_exec(bContext *C, wmOperator *op) ED_node_tree_pop(snode); } - WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_NODES, nullptr); return OPERATOR_FINISHED; } @@ -200,7 +200,8 @@ void NODE_OT_group_edit(wmOperatorType *ot) static AnimationBasePathChange *animation_basepath_change_new(const char *src_basepath, const char *dst_basepath) { - AnimationBasePathChange *basepath_change = MEM_callocN(sizeof(*basepath_change), AT); + AnimationBasePathChange *basepath_change = (AnimationBasePathChange *)MEM_callocN( + sizeof(*basepath_change), AT); basepath_change->src_basepath = src_basepath; basepath_change->dst_basepath = dst_basepath; return basepath_change; @@ -218,13 +219,13 @@ static void animation_basepath_change_free(AnimationBasePathChange *basepath_cha /* returns 1 if its OK */ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) { - /* clear new pointers, set in copytree */ + /* Clear new pointers, set in #ntreeCopyTree_ex_new_pointers. */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->new_node = NULL; + node->new_node = nullptr; } - ListBase anim_basepaths = {NULL, NULL}; - LinkNode *nodes_delayed_free = NULL; + ListBase anim_basepaths = {nullptr, nullptr}; + LinkNode *nodes_delayed_free = nullptr; bNodeTree *ngroup = (bNodeTree *)gnode->id; /* wgroup is a temporary copy of the NodeTree we're merging in @@ -247,7 +248,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* keep track of this node's RNA "base" path (the part of the path identifying the node) * if the old nodetree has animation data which potentially covers this node */ - const char *old_animation_basepath = NULL; + const char *old_animation_basepath = nullptr; if (wgroup->adt) { PointerRNA ptr; RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr); @@ -277,7 +278,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) node->flag |= NODE_SELECT; } - bNodeLink *glinks_first = ntree->links.last; + bNodeLink *glinks_first = (bNodeLink *)ntree->links.last; /* Add internal links to the ntree */ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) { @@ -285,10 +286,10 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) BLI_addtail(&ntree->links, link); } - bNodeLink *glinks_last = ntree->links.last; + bNodeLink *glinks_last = (bNodeLink *)ntree->links.last; /* and copy across the animation, - * note that the animation data's action can be NULL here */ + * note that the animation data's action can be nullptr here */ if (wgroup->adt) { bAction *waction; @@ -307,7 +308,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* free temp action too */ if (waction) { BKE_id_free(bmain, waction); - wgroup->adt->action = NULL; + wgroup->adt->action = nullptr; } } @@ -317,14 +318,14 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) /* restore external links to and from the gnode */ /* input links */ - if (glinks_first != NULL) { + if (glinks_first != nullptr) { for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) { if (link->fromnode->type == NODE_GROUP_INPUT) { const char *identifier = link->fromsock->identifier; int num_external_links = 0; /* find external links to this input */ - for (bNodeLink *tlink = ntree->links.first; tlink != glinks_first->next; + for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next; tlink = tlink->next) { if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); @@ -348,10 +349,11 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) } /* Also iterate over new links to cover passthrough links. */ - glinks_last = ntree->links.last; + glinks_last = (bNodeLink *)ntree->links.last; /* output links */ - for (bNodeLink *link = ntree->links.first; link != glinks_first->next; link = link->next) { + for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next; + link = link->next) { if (link->fromnode == gnode) { const char *identifier = link->fromsock->identifier; int num_internal_links = 0; @@ -384,7 +386,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) } while (nodes_delayed_free) { - bNode *node = BLI_linklist_pop(&nodes_delayed_free); + bNode *node = (bNode *)BLI_linklist_pop(&nodes_delayed_free); nodeRemoveNode(bmain, ntree, node, false); } @@ -455,10 +457,10 @@ static int node_group_separate_selected( /* clear new pointers, set in BKE_node_copy_ex(). */ LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - node->new_node = NULL; + node->new_node = nullptr; } - ListBase anim_basepaths = {NULL, NULL}; + ListBase anim_basepaths = {nullptr, nullptr}; /* add selected nodes into the ntree */ LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup->nodes) { @@ -543,7 +545,7 @@ static int node_group_separate_selected( } /* and copy across the animation, - * note that the animation data's action can be NULL here */ + * note that the animation data's action can be nullptr here */ if (ngroup->adt) { /* now perform the moving */ BKE_animdata_transfer_by_basepath(bmain, &ngroup->id, &ntree->id, &anim_basepaths); @@ -562,16 +564,16 @@ static int node_group_separate_selected( return 1; } -typedef enum eNodeGroupSeparateType { +enum eNodeGroupSeparateType { NODE_GS_COPY, NODE_GS_MOVE, -} eNodeGroupSeparateType; +}; /* Operator Property */ static const EnumPropertyItem node_group_separate_types[] = { {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"}, {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static int node_group_separate_exec(bContext *C, wmOperator *op) @@ -628,8 +630,8 @@ static int node_group_separate_invoke(bContext *C, uiLayout *layout = UI_popup_menu_layout(pup); uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); - uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_COPY); - uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_MOVE); + uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_COPY); + uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_MOVE); UI_popup_menu_end(C, pup); @@ -674,12 +676,12 @@ static bool node_group_make_test_selected(bNodeTree *ntree, int ok = true; /* make a local pseudo node tree to pass to the node poll functions */ - bNodeTree *ngroup = ntreeAddTree(NULL, "Pseudo Node Group", ntree_idname); + bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname); /* check poll functions for selected nodes */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node_group_make_use_node(node, gnode)) { - const char *disabled_hint = NULL; + const char *disabled_hint = nullptr; if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) { if (disabled_hint) { @@ -781,7 +783,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, expose_visible = true; } - ListBase anim_basepaths = {NULL, NULL}; + ListBase anim_basepaths = {nullptr, nullptr}; /* move nodes over */ LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { @@ -995,10 +997,10 @@ static bNode *node_group_make_from_selected(const bContext *C, Main *bmain = CTX_data_main(C); float min[2], max[2]; - const int totselect = node_get_selected_minmax(ntree, NULL, min, max, false); + const int totselect = node_get_selected_minmax(ntree, nullptr, min, max, false); /* don't make empty group */ if (totselect == 0) { - return NULL; + return nullptr; } /* new nodetree */ @@ -1029,7 +1031,7 @@ static int node_group_make_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - if (!node_group_make_test_selected(ntree, NULL, ntree_idname, op->reports)) { + if (!node_group_make_test_selected(ntree, nullptr, ntree_idname, op->reports)) { return OPERATOR_CANCELLED; } @@ -1042,7 +1044,7 @@ static int node_group_make_exec(bContext *C, wmOperator *op) if (ngroup) { ED_node_tree_push(snode, ngroup, gnode); LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - sort_multi_input_socket_links(snode, node, NULL, NULL); + sort_multi_input_socket_links(snode, node, nullptr, nullptr); } ntreeUpdateTree(bmain, ngroup); } diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.cc index 28c660b0632..57dc0b6fef2 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.cc @@ -55,12 +55,14 @@ #include "node_intern.h" /* own include */ -/* ****************** Relations helpers *********************** */ +/* -------------------------------------------------------------------- */ +/** \name Relations Helpers + * \{ */ static bool ntree_has_drivers(bNodeTree *ntree) { const AnimData *adt = BKE_animdata_from_id(&ntree->id); - if (adt == NULL) { + if (adt == nullptr) { return false; } return !BLI_listbase_is_empty(&adt->drivers); @@ -102,7 +104,7 @@ static bool node_group_has_output_dfs(bNode *node) return false; } ntree->id.tag |= LIB_TAG_DOIT; - for (bNode *current_node = ntree->nodes.first; current_node != NULL; + for (bNode *current_node = (bNode *)ntree->nodes.first; current_node != nullptr; current_node = current_node->next) { if (current_node->type == NODE_GROUP) { if (current_node->id && node_group_has_output_dfs(current_node)) { @@ -120,7 +122,7 @@ static bool node_group_has_output(Main *bmain, bNode *node) { BLI_assert(ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)); bNodeTree *ntree = (bNodeTree *)node->id; - if (ntree == NULL) { + if (ntree == nullptr) { return false; } BKE_main_id_tag_listbase(&bmain->nodetrees, LIB_TAG_DOIT, false); @@ -145,7 +147,7 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node) * is connected to and so eventually. */ if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - if (current_node->id != NULL && ntree_has_drivers((bNodeTree *)current_node->id)) { + if (current_node->id != nullptr && ntree_has_drivers((bNodeTree *)current_node->id)) { return true; } if (ntree_check_nodes_connected(ntree, node, current_node) && @@ -162,14 +164,18 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node) return false; } -/* ****************** Add *********************** */ +/** \} */ -typedef struct bNodeListItem { +/* -------------------------------------------------------------------- */ +/** \name Add Node + * \{ */ + +struct bNodeListItem { struct bNodeListItem *next, *prev; struct bNode *node; -} bNodeListItem; +}; -typedef struct NodeInsertOfsData { +struct NodeInsertOfsData { bNodeTree *ntree; bNode *insert; /* inserted node */ bNode *prev, *next; /* prev/next node in the chain */ @@ -178,7 +184,7 @@ typedef struct NodeInsertOfsData { wmTimer *anim_timer; float offset_x; /* offset to apply to node chain */ -} NodeInsertOfsData; +}; static void clear_picking_highlight(ListBase *links) { @@ -189,8 +195,8 @@ static void clear_picking_highlight(ListBase *links) static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock) { - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); + LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link"); linkdata->data = oplink; if (sock->in_out == SOCK_OUT) { oplink->fromnode = node; @@ -225,10 +231,10 @@ static void pick_link(const bContext *C, BLI_addtail(&nldrag->links, linkdata); nodeRemLink(snode->edittree, link_to_pick); - BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != NULL); + BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != nullptr); sort_multi_input_socket_links( - snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, NULL); + snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, nullptr); /* Send changed event to original link->tonode. */ if (node) { @@ -256,7 +262,7 @@ static void pick_input_link_by_link_intersect(const bContext *C, const int resolution = NODE_LINK_RESOL; - bNodeLink *link_to_pick = NULL; + bNodeLink *link_to_pick = nullptr; clear_picking_highlight(&snode->edittree->links); LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { if (link->tosock == socket) { @@ -305,8 +311,8 @@ static void pick_input_link_by_link_intersect(const bContext *C, static int sort_nodes_locx(const void *a, const void *b) { - const bNodeListItem *nli1 = a; - const bNodeListItem *nli2 = b; + const bNodeListItem *nli1 = (const bNodeListItem *)a; + const bNodeListItem *nli2 = (const bNodeListItem *)b; const bNode *node1 = nli1->node; const bNode *node2 = nli2->node; @@ -319,14 +325,14 @@ static int sort_nodes_locx(const void *a, const void *b) static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used) { if (nodeSocketIsHidden(sock)) { - return 0; + return false; } if (!allow_used && (sock->flag & SOCK_IN_USE)) { - return 0; + return false; } - return 1; + return true; } static bNodeSocket *best_socket_output(bNodeTree *ntree, @@ -374,10 +380,10 @@ static bNodeSocket *best_socket_output(bNodeTree *ntree, /* Always allow linking to an reroute node. The socket type of the reroute sockets might change * after the link has been created. */ if (node->type == NODE_REROUTE) { - return node->outputs.first; + return (bNodeSocket *)node->outputs.first; } - return NULL; + return nullptr; } /* this is a bit complicated, but designed to prioritize finding @@ -409,7 +415,7 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in } } - return NULL; + return nullptr; } static bool snode_autoconnect_input(SpaceNode *snode, @@ -430,10 +436,10 @@ static bool snode_autoconnect_input(SpaceNode *snode, return true; } -typedef struct LinkAndPosition { +struct LinkAndPosition { struct bNodeLink *link; float multi_socket_position[2]; -} LinkAndPosition; +}; static int compare_link_by_y_position(const void *a, const void *b) { @@ -457,14 +463,14 @@ void sort_multi_input_socket_links(SpaceNode *snode, } /* The total is calculated in #node_update_nodetree, which runs before this draw step. */ int total_inputs = socket->total_inputs + 1; - struct LinkAndPosition **input_links = MEM_malloc_arrayN( + struct LinkAndPosition **input_links = (LinkAndPosition **)MEM_malloc_arrayN( total_inputs, sizeof(LinkAndPosition *), __func__); int index = 0; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { if (link->tosock == socket) { - struct LinkAndPosition *link_and_position = MEM_callocN(sizeof(struct LinkAndPosition), - __func__); + struct LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN( + sizeof(struct LinkAndPosition), __func__); link_and_position->link = link; node_link_calculate_multi_input_position(link->tosock->locx, link->tosock->locy, @@ -477,7 +483,8 @@ void sort_multi_input_socket_links(SpaceNode *snode, } if (drag_link) { - LinkAndPosition *link_and_position = MEM_callocN(sizeof(LinkAndPosition), __func__); + LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN(sizeof(LinkAndPosition), + __func__); link_and_position->link = drag_link; copy_v2_v2(link_and_position->multi_socket_position, cursor); input_links[index] = link_and_position; @@ -505,11 +512,12 @@ static void snode_autoconnect(Main *bmain, const bool replace) { bNodeTree *ntree = snode->edittree; - ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list"); + ListBase *nodelist = (ListBase *)MEM_callocN(sizeof(ListBase), "items_list"); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & NODE_SELECT) { - bNodeListItem *nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item"); + bNodeListItem *nli = (bNodeListItem *)MEM_mallocN(sizeof(bNodeListItem), + "temporary node list item"); nli->node = node; BLI_addtail(nodelist, nli); } @@ -522,7 +530,7 @@ static void snode_autoconnect(Main *bmain, LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) { bool has_selected_inputs = false; - if (nli->next == NULL) { + if (nli->next == nullptr) { break; } @@ -536,7 +544,7 @@ static void snode_autoconnect(Main *bmain, /* if there are selected sockets, connect those */ LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) { if (sock_to->flag & SELECT) { - has_selected_inputs = 1; + has_selected_inputs = true; if (!socket_is_available(ntree, sock_to, replace)) { continue; @@ -588,14 +596,18 @@ static void snode_autoconnect(Main *bmain, MEM_freeN(nodelist); } -/* *************************** link viewer op ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Link Viewer Operator + * \{ */ static int node_link_viewer(const bContext *C, bNode *tonode) { SpaceNode *snode = CTX_wm_space_node(C); /* context check */ - if (tonode == NULL || BLI_listbase_is_empty(&tonode->outputs)) { + if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) { return OPERATOR_CANCELLED; } if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { @@ -603,7 +615,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode) } /* get viewer */ - bNode *viewer_node = NULL; + bNode *viewer_node = nullptr; LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (node->flag & NODE_DO_OUTPUT) { @@ -613,7 +625,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode) } } /* no viewer, we make one active */ - if (viewer_node == NULL) { + if (viewer_node == nullptr) { LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { node->flag |= NODE_DO_OUTPUT; @@ -623,14 +635,14 @@ static int node_link_viewer(const bContext *C, bNode *tonode) } } - bNodeSocket *sock = NULL; - bNodeLink *link = NULL; + bNodeSocket *sock = nullptr; + bNodeLink *link = nullptr; /* try to find an already connected socket to cycle to the next */ if (viewer_node) { - link = NULL; + link = nullptr; - for (link = snode->edittree->links.first; link; link = link->next) { + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { if (link->tonode == viewer_node && link->fromnode == tonode) { if (link->tosock == viewer_node->inputs.first) { break; @@ -663,7 +675,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode) /* find a socket starting from the first socket */ if (!sock) { - for (sock = tonode->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { if (!nodeSocketIsHidden(sock)) { break; } @@ -674,24 +686,25 @@ static int node_link_viewer(const bContext *C, bNode *tonode) /* add a new viewer if none exists yet */ if (!viewer_node) { /* XXX location is a quick hack, just place it next to the linked socket */ - viewer_node = node_add_node(C, NULL, CMP_NODE_VIEWER, sock->locx + 100, sock->locy); + viewer_node = node_add_node(C, nullptr, CMP_NODE_VIEWER, sock->locx + 100, sock->locy); if (!viewer_node) { return OPERATOR_CANCELLED; } - link = NULL; + link = nullptr; } else { /* get link to viewer */ - for (link = snode->edittree->links.first; link; link = link->next) { + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) { break; } } } - if (link == NULL) { - nodeAddLink(snode->edittree, tonode, sock, viewer_node, viewer_node->inputs.first); + if (link == nullptr) { + nodeAddLink( + snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first); } else { link->fromnode = tonode; @@ -741,7 +754,11 @@ void NODE_OT_link_viewer(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* *************************** add link op ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Link Operator + * \{ */ static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag)) { @@ -779,7 +796,7 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link) if (tlink && tlink->fromsock == from) { if (from_count > from_link_limit) { nodeRemLink(ntree, tlink); - tlink = NULL; + tlink = nullptr; from_count--; } } @@ -787,13 +804,13 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link) if (tlink && tlink->tosock == to) { if (to_count > to_link_limit) { nodeRemLink(ntree, tlink); - tlink = NULL; + tlink = nullptr; to_count--; } else if (tlink->fromsock == from) { /* Also remove link if it comes from the same output. */ nodeRemLink(ntree, tlink); - tlink = NULL; + tlink = nullptr; to_count--; from_count--; } @@ -806,13 +823,13 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNodeLinkDrag *nldrag = op->customdata; + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; bool do_tag_update = false; /* avoid updates while applying links */ ntree->is_updating = true; LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; + bNodeLink *link = (bNodeLink *)linkdata->data; /* See note below, but basically TEST flag means that the link * was connected to output (or to a node which affects the @@ -867,14 +884,14 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) { SpaceNode *snode = CTX_wm_space_node(C); - bNodeLinkDrag *nldrag = op->customdata; + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; if (nldrag->in_out == SOCK_OUT) { bNode *tnode; - bNodeSocket *tsock = NULL; + bNodeSocket *tsock = nullptr; if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; + bNodeLink *link = (bNodeLink *)linkdata->data; /* skip if socket is on the same node as the fromsock */ if (tnode && link->fromnode == tnode) { @@ -882,7 +899,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) } /* Skip if tsock is already linked with this output. */ - bNodeLink *existing_link_connected_to_fromsock = NULL; + bNodeLink *existing_link_connected_to_fromsock = nullptr; LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) { if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) { existing_link_connected_to_fromsock = existing_link; @@ -906,22 +923,22 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) } else { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; + bNodeLink *link = (bNodeLink *)linkdata->data; if (nldrag->last_node_hovered_while_dragging_a_link) { sort_multi_input_socket_links( - snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, cursor); + snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, cursor); } - link->tonode = NULL; - link->tosock = NULL; + link->tonode = nullptr; + link->tosock = nullptr; } } } else { bNode *tnode; - bNodeSocket *tsock = NULL; + bNodeSocket *tsock = nullptr; if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; + bNodeLink *link = (bNodeLink *)linkdata->data; /* skip if this is already the target socket */ if (link->fromsock == tsock) { @@ -939,10 +956,10 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) } else { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; + bNodeLink *link = (bNodeLink *)linkdata->data; - link->fromnode = NULL; - link->fromsock = NULL; + link->fromnode = nullptr; + link->fromsock = nullptr; } } } @@ -952,7 +969,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) /* in_out = starting socket */ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) { - bNodeLinkDrag *nldrag = op->customdata; + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; ARegion *region = CTX_wm_region(C); float cursor[2]; @@ -977,7 +994,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) { node_link_exit(C, op, true); - ED_workspace_status_text(C, NULL); + ED_workspace_status_text(C, nullptr); ED_region_tag_redraw(region); SpaceNode *snode = CTX_wm_space_node(C); clear_picking_highlight(&snode->edittree->links); @@ -993,13 +1010,13 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) /* return 1 when socket clicked */ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach) { - bNodeLinkDrag *nldrag = NULL; + bNodeLinkDrag *nldrag = nullptr; /* output indicated? */ bNode *node; bNodeSocket *sock; if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { - nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); + nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); const int num_links = nodeCountSocketLinks(snode->edittree, sock); int link_limit = nodeSocketLinkLimit(sock); @@ -1009,11 +1026,11 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor /* detach current links and store them in the operator data */ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { if (link->fromsock == sock) { - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); + LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link"); linkdata->data = oplink; *oplink = *link; - oplink->next = oplink->prev = NULL; + oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; /* The link could be disconnected and in that case we @@ -1043,7 +1060,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor } /* or an input? */ else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { - nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); + nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); nldrag->last_node_hovered_while_dragging_a_link = node; const int num_links = nodeCountSocketLinks(snode->edittree, sock); @@ -1061,12 +1078,12 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor } } - if (link_to_pick != NULL && !nldrag->from_multi_input_socket) { - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); + if (link_to_pick != nullptr && !nldrag->from_multi_input_socket) { + LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link"); linkdata->data = oplink; *oplink = *link_to_pick; - oplink->next = oplink->prev = NULL; + oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; oplink->flag &= ~NODE_LINK_TEST; if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { @@ -1127,7 +1144,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) static void node_link_cancel(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); - bNodeLinkDrag *nldrag = op->customdata; + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; BLI_remlink(&snode->runtime->linkdrag, nldrag); @@ -1167,7 +1184,7 @@ void NODE_OT_link(wmOperatorType *ot) RNA_def_float_array(ot->srna, "drag_start", 2, - 0, + nullptr, -UI_PRECISION_FLOAT_MAX, UI_PRECISION_FLOAT_MAX, "Drag Start", @@ -1178,7 +1195,11 @@ void NODE_OT_link(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -/* ********************** Make Link operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Link Operator + * \{ */ /* makes a link between selected output and input sockets */ static int node_make_link_exec(bContext *C, wmOperator *op) @@ -1189,11 +1210,11 @@ static int node_make_link_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - snode_autoconnect(bmain, snode, 1, replace); + snode_autoconnect(bmain, snode, true, replace); /* deselect sockets after linking */ - node_deselect_all_input_sockets(snode, 0); - node_deselect_all_output_sockets(snode, 0); + node_deselect_all_input_sockets(snode, false); + node_deselect_all_output_sockets(snode, false); ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); @@ -1218,27 +1239,37 @@ void NODE_OT_link_make(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean( - ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links"); + ot->srna, "replace", false, "Replace", "Replace socket connections with the new links"); } -/* ********************** Node Link Intersect ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Intersect + * \{ */ + static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot) { float coord_array[NODE_LINK_RESOL + 1][2]; - if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) { + if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { for (int i = 0; i < tot - 1; i++) { for (int b = 0; b < NODE_LINK_RESOL; b++) { if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) { - return 1; + return true; } } } } - return 0; + return false; } -/* ********************** Cut Link operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cut Link Operator + * \{ */ + static int cut_links_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1285,7 +1316,7 @@ static int cut_links_exec(bContext *C, wmOperator *op) snode_update(snode, link->tonode); bNode *to_node = link->tonode; nodeRemLink(snode->edittree, link); - sort_multi_input_socket_links(snode, to_node, NULL, NULL); + sort_multi_input_socket_links(snode, to_node, nullptr, nullptr); } } @@ -1324,13 +1355,17 @@ void NODE_OT_links_cut(wmOperatorType *ot) /* properties */ PropertyRNA *prop; prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); /* internal */ RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } -/* ********************** Mute links operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mute Links Operator + * \{ */ static int mute_links_exec(bContext *C, wmOperator *op) { @@ -1426,13 +1461,17 @@ void NODE_OT_links_mute(wmOperatorType *ot) /* properties */ PropertyRNA *prop; prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); /* internal */ RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } -/* ********************** Detach links operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Detach Links Operator + * \{ */ static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1469,7 +1508,11 @@ void NODE_OT_links_detach(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Set Parent ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Parent Operator + * \{ */ static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1491,7 +1534,7 @@ static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) } ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1511,7 +1554,11 @@ void NODE_OT_parent_set(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Join Nodes ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Join Nodes Operator + * \{ */ /* tags for depth-first search */ #define NODE_JOIN_DONE 1 @@ -1563,7 +1610,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) } } - bNode *frame = node_add_node(C, NULL, NODE_FRAME, 0.0f, 0.0f); + bNode *frame = node_add_node(C, nullptr, NODE_FRAME, 0.0f, 0.0f); /* reset tags */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -1584,7 +1631,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) } ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1604,7 +1651,11 @@ void NODE_OT_join(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Attach ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attach Operator + * \{ */ static bNode *node_find_frame_to_attach(ARegion *region, const bNodeTree *ntree, @@ -1624,7 +1675,7 @@ static bNode *node_find_frame_to_attach(ARegion *region, } } - return NULL; + return nullptr; } static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -1637,7 +1688,7 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent if (frame) { LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) { if (node->flag & NODE_SELECT) { - if (node->parent == NULL) { + if (node->parent == nullptr) { /* disallow moving a parent into its child */ if (nodeAttachNodeCheck(frame, node) == false) { /* attach all unparented nodes */ @@ -1666,7 +1717,7 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent } ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1687,7 +1738,11 @@ void NODE_OT_attach(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Detach ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Detach Operator + * \{ */ /* tags for depth-first search */ #define NODE_DETACH_DONE 1 @@ -1738,7 +1793,7 @@ static int node_detach_exec(bContext *C, wmOperator *UNUSED(op)) } ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; } @@ -1758,7 +1813,11 @@ void NODE_OT_detach(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************* automatic node insert on dragging ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Automatic Node Insert on Dragging + * \{ */ /* prevent duplicate testing code below */ static bool ed_node_link_conditions(ScrArea *area, @@ -1766,13 +1825,13 @@ static bool ed_node_link_conditions(ScrArea *area, SpaceNode **r_snode, bNode **r_select) { - SpaceNode *snode = area ? area->spacedata.first : NULL; + SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr; *r_snode = snode; - *r_select = NULL; + *r_select = nullptr; /* no unlucky accidents */ - if (area == NULL || area->spacetype != SPACE_NODE) { + if (area == nullptr || area->spacetype != SPACE_NODE) { return false; } @@ -1782,8 +1841,8 @@ static bool ed_node_link_conditions(ScrArea *area, } bNode *node; - bNode *select = NULL; - for (node = snode->edittree->nodes.first; node; node = node->next) { + bNode *select = nullptr; + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if (node->flag & SELECT) { if (select) { break; @@ -1792,7 +1851,7 @@ static bool ed_node_link_conditions(ScrArea *area, } } /* only one selected */ - if (node || select == NULL) { + if (node || select == nullptr) { return false; } @@ -1835,7 +1894,7 @@ void ED_node_link_intersect_test(ScrArea *area, int test) } /* find link to select/highlight */ - bNodeLink *selink = NULL; + bNodeLink *selink = nullptr; float dist_best = FLT_MAX; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { float coord_array[NODE_LINK_RESOL + 1][2]; @@ -1844,7 +1903,7 @@ void ED_node_link_intersect_test(ScrArea *area, int test) continue; } - if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) { + if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { float dist = FLT_MAX; /* loop over link coords to find shortest dist to @@ -1876,6 +1935,12 @@ void ED_node_link_intersect_test(ScrArea *area, int test) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Insert Offset Operator + * \{ */ + static int get_main_socket_priority(const bNodeSocket *socket) { switch ((eNodeSocketDatatype)socket->type) { @@ -1939,7 +2004,7 @@ static bNodeSocket *get_main_socket(ListBase *sockets) } } - return NULL; + return nullptr; } static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata)) @@ -1977,7 +2042,7 @@ static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, con #define NODE_INSOFS_ANIM_DURATION 0.25f /** - * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar + * Callback that applies #NodeInsertOfsData.offset_x to a node or its parent, similar * to node_link_insert_offset_output_chain_cb below, but with slightly different logic */ static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, @@ -1985,7 +2050,7 @@ static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, void *userdata, const bool reversed) { - NodeInsertOfsData *data = userdata; + NodeInsertOfsData *data = (NodeInsertOfsData *)userdata; bNode *ofs_node = reversed ? fromnode : tonode; if (ofs_node->parent && ofs_node->parent != data->insert_parent) { @@ -2022,7 +2087,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode, void *userdata, const bool reversed) { - NodeInsertOfsData *data = userdata; + NodeInsertOfsData *data = (NodeInsertOfsData *)userdata; bNode *ofs_node = reversed ? fromnode : tonode; if (data->insert_parent) { @@ -2035,7 +2100,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode, } if (nodeIsChildOf(data->insert_parent, ofs_node) == false) { - data->insert_parent = NULL; + data->insert_parent = nullptr; } } else if (ofs_node->parent) { @@ -2086,7 +2151,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, rctf totr_frame; /* check nodes front to back */ - for (frame = ntree->nodes.last; frame; frame = frame->prev) { + for (frame = (bNode *)ntree->nodes.last; frame; frame = frame->prev) { /* skip selected, those are the nodes we want to attach */ if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { continue; @@ -2152,7 +2217,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, iofsd->offset_x = margin; /* flag all parents of insert as offset to prevent them from being offset */ - nodeParentsIter(insert, node_parents_offset_flag_enable_cb, NULL); + nodeParentsIter(insert, node_parents_offset_flag_enable_cb, nullptr); /* iterate over entire chain and apply offsets */ nodeChainIter(ntree, right_alignment ? next : prev, @@ -2173,7 +2238,8 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w NodeInsertOfsData *iofsd = snode->runtime->iofsd; bool redraw = false; - if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) { + if (!snode || event->type != TIMER || iofsd == nullptr || + iofsd->anim_timer != event->customdata) { return OPERATOR_PASS_THROUGH; } @@ -2203,13 +2269,13 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w /* end timer + free insert offset data */ if (duration > NODE_INSOFS_ANIM_DURATION) { - WM_event_remove_timer(CTX_wm_manager(C), NULL, iofsd->anim_timer); + WM_event_remove_timer(CTX_wm_manager(C), nullptr, iofsd->anim_timer); LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { node->anim_init_locx = node->anim_ofsx = 0.0f; } - snode->runtime->iofsd = NULL; + snode->runtime->iofsd = nullptr; MEM_freeN(iofsd); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); @@ -2259,6 +2325,12 @@ void NODE_OT_insert_offset(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Note Link Insert + * \{ */ + /* assumes link with NODE_LINKFLAG_HILITE set */ void ED_node_link_insert(Main *bmain, ScrArea *area) { @@ -2270,7 +2342,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) /* get the link */ bNodeLink *link; - for (link = snode->edittree->links.first; link; link = link->next) { + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { if (link->flag & NODE_LINKFLAG_HILITE) { break; } @@ -2298,7 +2370,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) /* set up insert offset data, it needs stuff from here */ if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) { - NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__); + NodeInsertOfsData *iofsd = (NodeInsertOfsData *)MEM_callocN(sizeof(NodeInsertOfsData), + __func__); iofsd->insert = select; iofsd->prev = link->fromnode; @@ -2314,3 +2387,5 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) } } } + +/** \} */ diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.cc index de63aa07acb..41820cd813c 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.cc @@ -21,7 +21,8 @@ * \ingroup spnode */ -#include <stdlib.h> +#include <array> +#include <cstdlib> #include "DNA_node_types.h" #include "DNA_windowmanager_types.h" @@ -81,7 +82,7 @@ static bool has_workbench_in_texture_color(const wmWindowManager *wm, const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype == SPACE_VIEW3D) { - const View3D *v3d = area->spacedata.first; + const View3D *v3d = (const View3D *)area->spacedata.first; if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) { return true; @@ -100,28 +101,28 @@ static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my) { bNode *node; - for (node = ntree->nodes.last; node; node = node->prev) { + for (node = (bNode *)ntree->nodes.last; node; node = node->prev) { if (node->typeinfo->select_area_func) { if (node->typeinfo->select_area_func(node, mx, my)) { return node; } } } - return NULL; + return nullptr; } static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my) { bNode *node; - for (node = ntree->nodes.last; node; node = node->prev) { + for (node = (bNode *)ntree->nodes.last; node; node = node->prev) { if (node->typeinfo->tweak_area_func) { if (node->typeinfo->tweak_area_func(node, mx, my)) { return node; } } } - return NULL; + return nullptr; } static bool is_position_over_node_or_socket(SpaceNode *snode, float mouse[2]) @@ -168,18 +169,18 @@ void node_socket_deselect(bNode *node, bNodeSocket *sock, const bool deselect_no sock->flag &= ~SELECT; if (node && deselect_node) { - bool sel = 0; + bool sel = false; /* if no selected sockets remain, also deselect the node */ - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (sock->flag & SELECT) { - sel = 1; + sel = true; break; } } - for (sock = node->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { if (sock->flag & SELECT) { - sel = 1; + sel = true; break; } } @@ -205,7 +206,7 @@ void node_deselect_all(SpaceNode *snode) { bNode *node; - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { nodeSetSelected(node, false); } } @@ -220,16 +221,16 @@ void node_deselect_all_input_sockets(SpaceNode *snode, const bool deselect_nodes * We can do that more efficiently here. */ - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { int sel = 0; - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { sock->flag &= ~SELECT; } /* if no selected sockets remain, also deselect the node */ if (deselect_nodes) { - for (sock = node->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { if (sock->flag & SELECT) { sel = 1; break; @@ -253,18 +254,18 @@ void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_node * We can do that more efficiently here. */ - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { bool sel = false; - for (sock = node->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { sock->flag &= ~SELECT; } /* if no selected sockets remain, also deselect the node */ if (deselect_nodes) { - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (sock->flag & SELECT) { - sel = 1; + sel = true; break; } } @@ -289,7 +290,7 @@ static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act) bNode *node; bool changed = false; - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if ((node->flag & SELECT) == 0) { if (node->type == node_act->type) { nodeSetSelected(node, true); @@ -306,7 +307,7 @@ static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act) bNode *node; bool changed = false; - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if ((node->flag & SELECT) == 0) { if (compare_v3v3(node->color, node_act->color, 0.005f)) { nodeSetSelected(node, true); @@ -327,7 +328,7 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo const char *sep, *suf_act, *suf_curr; pref_len_act = BLI_str_partition_ex_utf8( - node_act->name, NULL, delims, &sep, &suf_act, from_right); + node_act->name, nullptr, delims, &sep, &suf_act, from_right); /* Note: in case we are searching for suffix, and found none, use whole name as suffix. */ if (from_right && !(sep && suf_act)) { @@ -335,12 +336,12 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo suf_act = node_act->name; } - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if (node->flag & SELECT) { continue; } pref_len_curr = BLI_str_partition_ex_utf8( - node->name, NULL, delims, &sep, &suf_curr, from_right); + node->name, nullptr, delims, &sep, &suf_curr, from_right); /* Same as with active node name! */ if (from_right && !(sep && suf_curr)) { @@ -371,7 +372,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNode *node_act = nodeGetActive(snode->edittree); - if (node_act == NULL) { + if (node_act == nullptr) { return OPERATOR_CANCELLED; } @@ -381,7 +382,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op) const int type = RNA_enum_get(op->ptr, "type"); if (!extend) { - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { nodeSetSelected(node, false); } } @@ -406,7 +407,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op) if (changed) { ED_node_sort(snode->edittree); - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); return OPERATOR_FINISHED; } @@ -420,7 +421,7 @@ void NODE_OT_select_grouped(wmOperatorType *ot) {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""}, {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""}, {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /* identifiers */ @@ -461,7 +462,7 @@ void node_select_single(bContext *C, bNode *node) bool active_texture_changed = false; bNode *tnode; - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { if (tnode != node) { nodeSetSelected(tnode, false); } @@ -476,7 +477,7 @@ void node_select_single(bContext *C, bNode *node) DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE); } - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } static int node_mouse_select(bContext *C, @@ -491,7 +492,7 @@ static int node_mouse_select(bContext *C, const Scene *scene = CTX_data_scene(C); const wmWindowManager *wm = CTX_wm_manager(C); bNode *node, *tnode; - bNodeSocket *sock = NULL; + bNodeSocket *sock = nullptr; bNodeSocket *tsock; float cursor[2]; int ret_value = OPERATOR_CANCELLED; @@ -530,7 +531,7 @@ static int node_mouse_select(bContext *C, /* Only allow one selected output per node, for sensible linking. * Allow selecting outputs from different nodes though, if extend is true. */ if (node) { - for (tsock = node->outputs.first; tsock; tsock = tsock->next) { + for (tsock = (bNodeSocket *)node->outputs.first; tsock; tsock = tsock->next) { if (tsock == sock) { continue; } @@ -538,11 +539,11 @@ static int node_mouse_select(bContext *C, } } if (!extend) { - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { if (tnode == node) { continue; } - for (tsock = tnode->outputs.first; tsock; tsock = tsock->next) { + for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) { node_socket_deselect(tnode, tsock, true); } } @@ -558,7 +559,7 @@ static int node_mouse_select(bContext *C, node = node_under_mouse_select(snode->edittree, (int)cursor[0], (int)cursor[1]); if (extend) { - if (node != NULL) { + if (node != nullptr) { /* If node is selected but not active, we want to make it active, * but not toggle (deselect) it. */ if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { @@ -567,7 +568,7 @@ static int node_mouse_select(bContext *C, ret_value = OPERATOR_FINISHED; } } - else if (deselect_all && node == NULL) { + else if (deselect_all && node == nullptr) { /* Rather than deselecting others, users may want to drag to box-select (drag from empty * space) or tweak-translate an already selected item. If these cases may apply, delay * deselection. */ @@ -576,13 +577,13 @@ static int node_mouse_select(bContext *C, } else { /* Deselect in empty space. */ - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { nodeSetSelected(tnode, false); } ret_value = OPERATOR_FINISHED; } } - else if (node != NULL) { + else if (node != nullptr) { /* When clicking on an already selected node, we want to wait to deselect * others and allow the user to start moving the node without that. */ if (wait_to_deselect_others && (node->flag & SELECT)) { @@ -591,7 +592,7 @@ static int node_mouse_select(bContext *C, else { nodeSetSelected(node, true); - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { if (tnode != node) { nodeSetSelected(tnode, false); } @@ -605,7 +606,7 @@ static int node_mouse_select(bContext *C, /* update node order */ if (ret_value != OPERATOR_CANCELLED) { bool active_texture_changed = false; - if (node != NULL && ret_value != OPERATOR_RUNNING_MODAL) { + if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) { ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed); } ED_node_set_active_viewer_key(snode); @@ -614,7 +615,7 @@ static int node_mouse_select(bContext *C, DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE); } - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } return ret_value; @@ -681,7 +682,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); + const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); @@ -693,7 +694,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op) is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr); } else { - is_inside = BLI_rctf_isect(&rectf, &node->totr, NULL); + is_inside = BLI_rctf_isect(&rectf, &node->totr, nullptr); } if (is_inside) { @@ -703,7 +704,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op) ED_node_sort(snode->edittree); - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); return OPERATOR_FINISHED; } @@ -740,7 +741,7 @@ void NODE_OT_select_box(wmOperatorType *ot) /* properties */ RNA_def_boolean(ot->srna, "tweak", - 0, + false, "Tweak", "Only activate when mouse is not over a node (useful for tweak gesture)"); @@ -766,8 +767,9 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) float zoom = (float)(BLI_rcti_size_x(®ion->winrct)) / (float)(BLI_rctf_size_x(®ion->v2d.cur)); - const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), - WM_gesture_is_modal_first(op->customdata)); + const eSelectOp sel_op = ED_select_op_modal( + (eSelectOp)RNA_enum_get(op->ptr, "mode"), + WM_gesture_is_modal_first((const wmGesture *)op->customdata)); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); @@ -780,13 +782,13 @@ static int node_circleselect_exec(bContext *C, wmOperator *op) UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) { nodeSetSelected(node, select); } } - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); return OPERATOR_FINISHED; } @@ -853,7 +855,7 @@ static bool do_lasso_select_node(bContext *C, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); /* do actual selection */ - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if (select && (node->flag & NODE_SELECT)) { continue; @@ -873,7 +875,7 @@ static bool do_lasso_select_node(bContext *C, } if (changed) { - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } return changed; @@ -885,7 +887,7 @@ static int node_lasso_select_exec(bContext *C, wmOperator *op) const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); if (mcoords) { - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); + const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); do_lasso_select_node(C, mcoords, mcoords_len, sel_op); @@ -916,7 +918,7 @@ void NODE_OT_select_lasso(wmOperatorType *ot) /* properties */ RNA_def_boolean(ot->srna, "tweak", - 0, + false, "Tweak", "Only activate when mouse is not over a node (useful for tweak gesture)"); @@ -940,7 +942,7 @@ static int node_select_all_exec(bContext *C, wmOperator *op) ED_node_sort(snode->edittree); - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); return OPERATOR_FINISHED; } @@ -973,11 +975,11 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) bNodeLink *link; bNode *node; - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { node->flag &= ~NODE_TEST; } - for (link = snode->edittree->links.first; link; link = link->next) { + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { if (nodeLinkIsHidden(link)) { continue; } @@ -986,7 +988,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) } } - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if (node->flag & NODE_TEST) { nodeSetSelected(node, true); } @@ -994,7 +996,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) ED_node_sort(snode->edittree); - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); return OPERATOR_FINISHED; } @@ -1025,11 +1027,11 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) bNodeLink *link; bNode *node; - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { node->flag &= ~NODE_TEST; } - for (link = snode->edittree->links.first; link; link = link->next) { + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { if (nodeLinkIsHidden(link)) { continue; } @@ -1038,7 +1040,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) } } - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if (node->flag & NODE_TEST) { nodeSetSelected(node, true); } @@ -1046,7 +1048,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) ED_node_sort(snode->edittree); - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); return OPERATOR_FINISHED; } @@ -1079,7 +1081,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op) bNode *active = nodeGetActive(snode->edittree); int totnodes; const bool revert = RNA_boolean_get(op->ptr, "prev"); - const bool same_type = 1; + const bool same_type = true; ntreeGetDependencyList(snode->edittree, &node_array, &totnodes); @@ -1093,9 +1095,9 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op) } if (same_type) { - bNode *node = NULL; + bNode *node = nullptr; - while (node == NULL) { + while (node == nullptr) { if (revert) { a--; } @@ -1112,7 +1114,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op) if (node->type == active->type) { break; } - node = NULL; + node = nullptr; } if (node) { active = node; @@ -1168,7 +1170,7 @@ void NODE_OT_select_same_type_step(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "prev", 0, "Previous", ""); + RNA_def_boolean(ot->srna, "prev", false, "Previous", ""); } /** \} */ @@ -1223,7 +1225,7 @@ static void node_find_update_fn(const struct bContext *C, static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2) { SpaceNode *snode = CTX_wm_space_node(C); - bNode *active = arg2; + bNode *active = (bNode *)arg2; if (active) { ARegion *region = CTX_wm_region(C); @@ -1261,7 +1263,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op) 0, ""); UI_but_func_search_set( - but, NULL, node_find_update_fn, op->type, false, NULL, node_find_exec_fn, NULL); + but, nullptr, node_find_update_fn, op->type, false, nullptr, node_find_exec_fn, nullptr); UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); /* fake button, it holds space for search items */ @@ -1273,22 +1275,23 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op) 10 - UI_searchbox_size_y(), UI_searchbox_size_x(), UI_searchbox_size_y(), - NULL, + nullptr, 0, 0, 0, 0, - NULL); + nullptr); /* Move it downwards, mouse over button. */ - UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, (const int[2]){0, -UI_UNIT_Y}); + std::array<int, 2> bounds_offset = {0, -UI_UNIT_Y}; + UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, bounds_offset.data()); return block; } static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - UI_popup_block_invoke(C, node_find_menu, op, NULL); + UI_popup_block_invoke(C, node_find_menu, op, nullptr); return OPERATOR_CANCELLED; } @@ -1306,7 +1309,7 @@ void NODE_OT_find_node(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "prev", 0, "Previous", ""); + RNA_def_boolean(ot->srna, "prev", false, "Previous", ""); } /** \} */ diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.cc index c880f3e99d8..fae180ca6c5 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.cc @@ -18,8 +18,8 @@ * \ingroup edinterface */ -#include <stdlib.h> -#include <string.h> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -29,6 +29,7 @@ #include "BLI_array.h" #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_vector.hh" #include "BLT_translation.h" @@ -51,7 +52,7 @@ /************************* Node Socket Manipulation **************************/ /* describes an instance of a node type and a specific socket to link */ -typedef struct NodeLinkItem { +struct NodeLinkItem { int socket_index; /* index for linking */ int socket_type; /* socket type for compatibility check */ const char *socket_name; /* ui label of the socket */ @@ -59,7 +60,7 @@ typedef struct NodeLinkItem { /* extra settings */ bNodeTree *ngroup; /* group node tree */ -} NodeLinkItem; +}; /* Compare an existing node to a link item to see if it can be reused. * item must be for the same node type! @@ -98,7 +99,7 @@ static void node_tag_recursive(bNode *node) node->flag |= NODE_TEST; - for (input = node->inputs.first; input; input = input->next) { + for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { if (input->link) { node_tag_recursive(input->link->fromnode); } @@ -115,7 +116,7 @@ static void node_clear_recursive(bNode *node) node->flag &= ~NODE_TEST; - for (input = node->inputs.first; input; input = input->next) { + for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { if (input->link) { node_clear_recursive(input->link->fromnode); } @@ -132,16 +133,16 @@ static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node) } /* tag linked nodes to be removed */ - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { node->flag &= ~NODE_TEST; } node_tag_recursive(rem_node); /* clear tags on nodes that are still used by other nodes */ - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { if (!(node->flag & NODE_TEST)) { - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (sock->link && sock->link->fromnode != rem_node) { node_clear_recursive(sock->link->fromnode); } @@ -150,7 +151,7 @@ static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node) } /* remove nodes */ - for (node = ntree->nodes.first; node; node = next) { + for (node = (bNode *)ntree->nodes.first; node; node = next) { next = node->next; if (node->flag & NODE_TEST) { @@ -205,7 +206,7 @@ static void node_socket_add_replace(const bContext *C, Main *bmain = CTX_data_main(C); bNode *node_from; bNodeSocket *sock_from_tmp; - bNode *node_prev = NULL; + bNode *node_prev = nullptr; /* unlink existing node */ if (sock_to->link) { @@ -214,7 +215,7 @@ static void node_socket_add_replace(const bContext *C, } /* find existing node that we can use */ - for (node_from = ntree->nodes.first; node_from; node_from = node_from->next) { + for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) { if (node_from->type == type) { break; } @@ -223,7 +224,7 @@ static void node_socket_add_replace(const bContext *C, if (node_from) { if (node_from->inputs.first || node_from->typeinfo->draw_buttons || node_from->typeinfo->draw_buttons_ex) { - node_from = NULL; + node_from = nullptr; } } @@ -233,7 +234,7 @@ static void node_socket_add_replace(const bContext *C, } else if (!node_from) { node_from = nodeAddStaticNode(C, ntree, type); - if (node_prev != NULL) { + if (node_prev != nullptr) { /* If we're replacing existing node, use its location. */ node_from->locx = node_prev->locx; node_from->locy = node_prev->locy; @@ -241,7 +242,7 @@ static void node_socket_add_replace(const bContext *C, node_from->offsety = node_prev->offsety; } else { - sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index); + sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index); nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to); } @@ -251,7 +252,7 @@ static void node_socket_add_replace(const bContext *C, nodeSetActive(ntree, node_from); /* add link */ - sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index); + sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index); nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to); sock_to->flag &= ~SOCK_COLLAPSED; @@ -259,8 +260,10 @@ static void node_socket_add_replace(const bContext *C, if (node_prev && node_from != node_prev) { bNodeSocket *sock_prev, *sock_from; - for (sock_prev = node_prev->inputs.first; sock_prev; sock_prev = sock_prev->next) { - for (sock_from = node_from->inputs.first; sock_from; sock_from = sock_from->next) { + for (sock_prev = (bNodeSocket *)node_prev->inputs.first; sock_prev; + sock_prev = sock_prev->next) { + for (sock_from = (bNodeSocket *)node_from->inputs.first; sock_from; + sock_from = sock_from->next) { if (nodeCountSocketLinks(ntree, sock_from) >= nodeSocketLinkLimit(sock_from)) { continue; } @@ -282,7 +285,7 @@ static void node_socket_add_replace(const bContext *C, if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE && /* White noise texture node does not have NodeTexBase. */ - node_from->storage != NULL && node_prev->storage != NULL) { + node_from->storage != nullptr && node_prev->storage != nullptr) { memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); } @@ -303,7 +306,7 @@ static void node_socket_add_replace(const bContext *C, #define UI_NODE_LINK_DISCONNECT -1 #define UI_NODE_LINK_REMOVE -2 -typedef struct NodeLinkArg { +struct NodeLinkArg { Main *bmain; Scene *scene; bNodeTree *ntree; @@ -314,7 +317,7 @@ typedef struct NodeLinkArg { NodeLinkItem item; uiLayout *layout; -} NodeLinkArg; +}; static void ui_node_link_items(NodeLinkArg *arg, int in_out, @@ -322,14 +325,15 @@ static void ui_node_link_items(NodeLinkArg *arg, int *r_totitems) { /* XXX this should become a callback for node types! */ - NodeLinkItem *items = NULL; + NodeLinkItem *items = nullptr; int totitems = 0; if (arg->node_type->type == NODE_GROUP) { bNodeTree *ngroup; int i; - for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) { + for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; + ngroup = (bNodeTree *)ngroup->id.next) { const char *disabled_hint; if ((ngroup->type != arg->ntree->type) || !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { @@ -341,10 +345,11 @@ static void ui_node_link_items(NodeLinkArg *arg, } if (totitems > 0) { - items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); + items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); i = 0; - for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) { + for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; + ngroup = (bNodeTree *)ngroup->id.next) { const char *disabled_hint; if ((ngroup->type != arg->ntree->type) || !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { @@ -354,7 +359,8 @@ static void ui_node_link_items(NodeLinkArg *arg, ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); bNodeSocket *stemp; int index; - for (stemp = lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) { + for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; + stemp = stemp->next, index++, i++) { NodeLinkItem *item = &items[i]; item->socket_index = index; @@ -380,7 +386,7 @@ static void ui_node_link_items(NodeLinkArg *arg, } if (totitems > 0) { - items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); + items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); i = 0; for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { @@ -474,15 +480,14 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) bNodeTree *ntree = arg->ntree; bNodeSocket *sock = arg->sock; uiLayout *layout = arg->layout; - uiLayout *column = NULL; + uiLayout *column = nullptr; uiBlock *block = uiLayoutGetBlock(layout); uiBut *but; NodeLinkArg *argN; int first = 1; /* generate array of node types sorted by UI name */ - bNodeType **sorted_ntypes = NULL; - BLI_array_declare(sorted_ntypes); + blender::Vector<bNodeType *> sorted_ntypes; NODE_TYPES_BEGIN (ntype) { const char *disabled_hint; @@ -498,20 +503,20 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) continue; } - BLI_array_append(sorted_ntypes, ntype); + sorted_ntypes.append(ntype); } NODE_TYPES_END; qsort( - sorted_ntypes, BLI_array_len(sorted_ntypes), sizeof(bNodeType *), ui_node_item_name_compare); + sorted_ntypes.data(), sorted_ntypes.size(), sizeof(bNodeType *), ui_node_item_name_compare); /* generate UI */ - for (int j = 0; j < BLI_array_len(sorted_ntypes); j++) { + for (int j = 0; j < sorted_ntypes.size(); j++) { bNodeType *ntype = sorted_ntypes[j]; NodeLinkItem *items; int totitems; char name[UI_MAX_NAME_STR]; - const char *cur_node_name = NULL; + const char *cur_node_name = nullptr; int num = 0; int icon = ICON_NONE; @@ -531,11 +536,11 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) } if (first) { - column = uiLayoutColumn(layout, 0); + column = uiLayoutColumn(layout, false); UI_block_layout_set_current(block, column); uiItemL(column, IFACE_(cname), ICON_NODE); - but = block->buttons.last; + but = (uiBut *)block->buttons.last; first = 0; } @@ -553,7 +558,7 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) 0, UI_UNIT_X * 4, UI_UNIT_Y, - NULL, + nullptr, 0.0, 0.0, 0.0, @@ -578,24 +583,22 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) 0, UI_UNIT_X * 4, UI_UNIT_Y, - NULL, + nullptr, 0.0, 0.0, 0.0, 0.0, TIP_("Add node to input")); - argN = MEM_dupallocN(arg); + argN = (NodeLinkArg *)MEM_dupallocN(arg); argN->item = items[i]; - UI_but_funcN_set(but, ui_node_link, argN, NULL); + UI_but_funcN_set(but, ui_node_link, argN, nullptr); } if (items) { MEM_freeN(items); } } - - BLI_array_free(sorted_ntypes); } static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name) @@ -635,7 +638,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ if (sock->link) { uiItemL(column, IFACE_("Link"), ICON_NONE); - but = block->buttons.last; + but = (uiBut *)block->buttons.last; but->drawflag = UI_BUT_TEXT_LEFT; but = uiDefBut(block, @@ -646,7 +649,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ 0, UI_UNIT_X * 4, UI_UNIT_Y, - NULL, + nullptr, 0.0, 0.0, 0.0, @@ -662,7 +665,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ 0, UI_UNIT_X * 4, UI_UNIT_Y, - NULL, + nullptr, 0.0, 0.0, 0.0, @@ -683,7 +686,7 @@ void uiTemplateNodeLink( uiBut *but; float socket_col[4]; - arg = MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); + arg = (NodeLinkArg *)MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); arg->ntree = ntree; arg->node = node; arg->sock = input; @@ -698,11 +701,11 @@ void uiTemplateNodeLink( char name[UI_MAX_NAME_STR]; ui_node_sock_name(ntree, input, name); but = uiDefMenuBut( - block, ui_template_node_link_menu, NULL, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, ""); + block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, ""); } else { but = uiDefIconMenuBut( - block, ui_template_node_link_menu, NULL, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, ""); + block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, ""); } UI_but_type_set_menu_from_pulldown(but); @@ -739,7 +742,7 @@ static void ui_node_draw_node( } } - for (input = node->inputs.first; input; input = input->next) { + for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { ui_node_draw_input(layout, C, ntree, node, input, depth + 1); } } @@ -749,7 +752,7 @@ static void ui_node_draw_input( { PointerRNA inputptr, nodeptr; uiBlock *block = uiLayoutGetBlock(layout); - uiLayout *row = NULL; + uiLayout *row = nullptr; bNode *lnode; bool dependency_loop; @@ -759,11 +762,11 @@ static void ui_node_draw_input( /* to avoid eternal loops on cyclic dependencies */ node->flag |= NODE_TEST; - lnode = (input->link) ? input->link->fromnode : NULL; + lnode = (input->link) ? input->link->fromnode : nullptr; dependency_loop = (lnode && (lnode->flag & NODE_TEST)); if (dependency_loop) { - lnode = NULL; + lnode = nullptr; } /* socket RNA pointer */ @@ -862,7 +865,7 @@ static void ui_node_draw_input( } if (add_dummy_decorator) { - uiItemDecoratorR(split_wrapper.decorate_column, NULL, NULL, 0); + uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0); } /* clear */ @@ -879,7 +882,7 @@ void uiTemplateNodeView( } /* clear for cycle check */ - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) { tnode->flag &= ~NODE_TEST; } diff --git a/source/blender/editors/space_node/node_toolbar.c b/source/blender/editors/space_node/node_toolbar.cc index 2e7d6ab6cd5..2e7d6ab6cd5 100644 --- a/source/blender/editors/space_node/node_toolbar.c +++ b/source/blender/editors/space_node/node_toolbar.cc diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.cc index 8ecab92aa26..f0db0539c4f 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.cc @@ -76,7 +76,7 @@ int space_node_view_flag( BLI_rctf_init_minmax(&cur_new); if (snode->edittree) { - for (node = snode->edittree->nodes.first; node; node = node->next) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { if ((node->flag & node_flag) == node_flag) { BLI_rctf_union(&cur_new, &node->totr); tot++; @@ -191,16 +191,16 @@ void NODE_OT_view_selected(wmOperatorType *ot) /** \name Background Image Operators * \{ */ -typedef struct NodeViewMove { +struct NodeViewMove { int mvalo[2]; int xmin, ymin, xmax, ymax; -} NodeViewMove; +}; static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) { SpaceNode *snode = CTX_wm_space_node(C); ARegion *region = CTX_wm_region(C); - NodeViewMove *nvm = op->customdata; + NodeViewMove *nvm = (NodeViewMove *)op->customdata; switch (event->type) { case MOUSEMOVE: @@ -215,8 +215,8 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e CLAMP(snode->yof, nvm->ymin, nvm->ymax); ED_region_tag_redraw(region); - WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); break; @@ -225,7 +225,7 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e case RIGHTMOUSE: if (event->val == KM_RELEASE) { MEM_freeN(nvm); - op->customdata = NULL; + op->customdata = nullptr; return OPERATOR_FINISHED; } break; @@ -247,14 +247,14 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent * void *lock; ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); - if (ibuf == NULL) { + if (ibuf == nullptr) { BKE_image_release_ibuf(ima, ibuf, lock); return OPERATOR_CANCELLED; } - nvm = MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct"); + nvm = (NodeViewMove *)MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct"); op->customdata = nvm; nvm->mvalo[0] = event->mval[0]; nvm->mvalo[1] = event->mval[1]; @@ -275,7 +275,7 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent * static void snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op) { MEM_freeN(op->customdata); - op->customdata = NULL; + op->customdata = nullptr; } void NODE_OT_backimage_move(wmOperatorType *ot) @@ -309,8 +309,8 @@ static int backimage_zoom_exec(bContext *C, wmOperator *op) snode->zoom *= fac; ED_region_tag_redraw(region); - WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); return OPERATOR_FINISHED; } @@ -356,9 +356,9 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op)) float facx, facy; ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); - if ((ibuf == NULL) || (ibuf->x == 0) || (ibuf->y == 0)) { + if ((ibuf == nullptr) || (ibuf->x == 0) || (ibuf->y == 0)) { BKE_image_release_ibuf(ima, ibuf, lock); return OPERATOR_CANCELLED; } @@ -374,8 +374,8 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op)) snode->yof = 0; ED_region_tag_redraw(region); - WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); return OPERATOR_FINISHED; } @@ -402,7 +402,7 @@ void NODE_OT_backimage_fit(wmOperatorType *ot) /** \name Sample Backdrop Operator * \{ */ -typedef struct ImageSampleInfo { +struct ImageSampleInfo { ARegionType *art; void *draw_handle; int x, y; @@ -420,12 +420,12 @@ typedef struct ImageSampleInfo { int draw; int color_manage; -} ImageSampleInfo; +}; static void sample_draw(const bContext *C, ARegion *region, void *arg_info) { Scene *scene = CTX_data_scene(C); - ImageSampleInfo *info = arg_info; + ImageSampleInfo *info = (ImageSampleInfo *)arg_info; if (info->draw) { ED_image_draw_info(scene, @@ -445,7 +445,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info) /* Returns mouse position in image space. */ bool ED_space_node_get_position( - Main *bmain, SpaceNode *snode, struct ARegion *ar, const int mval[2], float fpos[2]) + Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2]) { if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) { return false; @@ -453,7 +453,7 @@ bool ED_space_node_get_position( void *lock; Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (!ibuf) { BKE_image_release_ibuf(ima, ibuf, lock); return false; @@ -462,8 +462,10 @@ bool ED_space_node_get_position( /* map the mouse coords to the backdrop image space */ float bufx = ibuf->x * snode->zoom; float bufy = ibuf->y * snode->zoom; - fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f); - fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f); + fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : + 0.0f); + fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : + 0.0f); BKE_image_release_ibuf(ima, ibuf, lock); return true; @@ -489,7 +491,7 @@ bool ED_space_node_color_sample( } ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (!ibuf) { return false; } @@ -532,14 +534,14 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); ARegion *region = CTX_wm_region(C); - ImageSampleInfo *info = op->customdata; + ImageSampleInfo *info = (ImageSampleInfo *)op->customdata; void *lock; Image *ima; ImBuf *ibuf; float fx, fy, bufx, bufy; ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (!ibuf) { info->draw = 0; return; @@ -570,8 +572,8 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) info->draw = 1; info->channels = ibuf->channels; - info->zp = NULL; - info->zfp = NULL; + info->zp = nullptr; + info->zfp = nullptr; if (ibuf->rect) { cp = (uchar *)(ibuf->rect + y * ibuf->x + x); @@ -616,7 +618,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) } else { info->draw = 0; - ED_node_sample_set(NULL); + ED_node_sample_set(nullptr); } BKE_image_release_ibuf(ima, ibuf, lock); @@ -626,9 +628,9 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) static void sample_exit(bContext *C, wmOperator *op) { - ImageSampleInfo *info = op->customdata; + ImageSampleInfo *info = (ImageSampleInfo *)op->customdata; - ED_node_sample_set(NULL); + ED_node_sample_set(nullptr); ED_region_draw_cb_exit(info->art, info->draw_handle); ED_area_tag_redraw(CTX_wm_area(C)); MEM_freeN(info); @@ -644,7 +646,7 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); + info = (ImageSampleInfo *)MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); info->art = region->type; info->draw_handle = ED_region_draw_cb_activate( region->type, sample_draw, info, REGION_DRAW_POST_PIXEL); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index c532dd8accd..f809bb13b42 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -740,7 +740,7 @@ static void id_local_fn(bContext *C, BKE_lib_id_clear_library_data(bmain, tselem->id); } else { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } } else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { @@ -846,13 +846,13 @@ static void id_override_library_create_fn(bContext *C, te->store_elem->id->tag |= LIB_TAG_DOIT; } success = BKE_lib_override_library_create( - bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference); + bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL); } else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != NULL; /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index bed7683703f..90389fc1be2 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1925,5 +1925,5 @@ void outliner_build_tree(Main *mainvar, outliner_filter_tree(space_outliner, view_layer); outliner_restore_scrolling_position(space_outliner, region, &focus); - BKE_main_id_clear_newpoins(mainvar); + BKE_main_id_newptr_and_tag_clear(mainvar); } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index cd75bc98b15..ac31e0e7c37 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -158,7 +158,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) ot->prop = RNA_def_boolean(ot->srna, "set_view_transform", true, - "Set view transform", + "Set View Transform", "Set appropriate view transform based on media colorspace"); } } @@ -1081,8 +1081,7 @@ static int sequencer_add_image_strip_invoke(bContext *C, sequencer_disable_one_time_properties(C, op); - const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; - RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method); + RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene)); /* Name set already by drag and drop. */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 11614d94862..1e0ecfd890e 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -111,10 +111,10 @@ void sequencer_buttons_register(ARegionType *art) pt = MEM_callocN(sizeof(PanelType), "spacetype sequencer panel metadata"); strcpy(pt->idname, "SEQUENCER_PT_metadata"); strcpy(pt->label, N_("Metadata")); + strcpy(pt->category, "Metadata"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->poll = metadata_panel_context_poll; pt->draw = metadata_panel_context_draw; - pt->flag |= PANEL_TYPE_DEFAULT_CLOSED; pt->order = 10; BLI_addtail(&art->paneltypes, pt); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ebd4c0090b4..f22a515a8f2 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1582,21 +1582,29 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); - ListBase nseqbase = {NULL, NULL}; - if (ed == NULL) { return OPERATOR_CANCELLED; } - SEQ_sequence_base_dupli_recursive(scene, scene, &nseqbase, ed->seqbasep, SEQ_DUPE_CONTEXT, 0); + Sequence *active_seq = SEQ_select_active_get(scene); + ListBase duplicated = {NULL, NULL}; - if (nseqbase.first) { - Sequence *seq = nseqbase.first; + SEQ_sequence_base_dupli_recursive(scene, scene, &duplicated, ed->seqbasep, 0, 0); + ED_sequencer_deselect_all(scene); + + if (duplicated.first) { + Sequence *seq = duplicated.first; /* Rely on the nseqbase list being added at the end. * Their UUIDs has been re-generated by the SEQ_sequence_base_dupli_recursive(), */ - BLI_movelisttolist(ed->seqbasep, &nseqbase); + BLI_movelisttolist(ed->seqbasep, &duplicated); + /* Handle duplicated strips: set active, select, ensure unique name and duplicate animation + * data. */ for (; seq; seq = seq->next) { + if (active_seq != NULL && STREQ(seq->name, active_seq->name)) { + SEQ_select_active_set(scene, seq); + } + seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK); SEQ_ensure_unique_name(seq, scene); } @@ -3053,7 +3061,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) scene->r.efra = efra; } - WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME_RANGE, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index cfc11afce13..7e515271b13 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -42,6 +42,7 @@ #include "SEQ_iterator.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "SEQ_transform.h" /* For menu, popup, icons, etc. */ @@ -807,7 +808,7 @@ static bool select_linked_internal(Scene *scene) bool changed = false; LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT) != 0) { + if ((seq->flag & SELECT) == 0) { continue; } /* Only get unselected neighbors. */ @@ -1187,7 +1188,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) test = (timeline_frame <= seq->startdisp); break; case 2: - test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp); + test = SEQ_time_strip_intersects_frame(seq, timeline_frame); break; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index d1e80f1d87e..c9b73aabf96 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -50,7 +50,7 @@ class CellValue { std::optional<bool> value_bool; std::optional<float2> value_float2; std::optional<float3> value_float3; - std::optional<Color4f> value_color; + std::optional<ColorGeometry4f> value_color; std::optional<ObjectCellValue> value_object; std::optional<CollectionCellValue> value_collection; }; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 02ffa1259fc..452885959f6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -116,7 +116,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - Color4f value; + ColorGeometry4f value; varray->get(index, &value); r_cell_value.value_color = value; }, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index f1ca65817f6..8079763a339 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -170,7 +170,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { this->draw_float_vector(params, Span(&value.x, 3)); } else if (cell_value.value_color.has_value()) { - const Color4f value = *cell_value.value_color; + const ColorGeometry4f value = *cell_value.value_color; this->draw_float_vector(params, Span(&value.r, 4)); } else if (cell_value.value_object.has_value()) { diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 0a89b7d0292..4a595c716b6 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2039,6 +2039,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, v3d.shading.type = drawtype; v3d.flag2 = V3D_HIDE_OVERLAYS; + /* HACK: When rendering gpencil objects this opacity is used to mix vertex colors in when not in + * render mode. */ + v3d.overlay.gpencil_vertex_paint_opacity = 1.0f; if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { v3d.flag2 |= V3D_SHOW_ANNOTATION; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 967ad966320..8b6d0e9ee04 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3666,8 +3666,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* convert border to 3d coordinates */ - if ((!ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) || - (!ED_view3d_unproject(region, rect.xmin, rect.ymin, depth_close, p_corner))) { + if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) || + (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) { return OPERATOR_CANCELLED; } @@ -3690,7 +3690,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) new_dist = rv3d->dist; /* convert the drawn rectangle into 3d space */ - if (depth_close != FLT_MAX && ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) { + if (depth_close != FLT_MAX && + ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) { negate_v3_v3(new_ofs, p); } else { 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 298a2a7a824..07c3b6bd1d8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -154,10 +154,10 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int * Only pre-select a vertex when the cursor is really close to it. */ if (eve_test) { BMVert *vert = (BMVert *)eve_test; - float vert_p_co[3], vert_co[3]; + float vert_p_co[2], vert_co[3]; const float mval_f[2] = {UNPACK2(vc.mval)}; mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); - ED_view3d_project(vc.region, vert_co, vert_p_co); + ED_view3d_project_v2(vc.region, vert_co, vert_p_co); float len = len_v2v2(vert_p_co, mval_f); if (len < 35) { best.ele = (BMElem *)eve_test; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 24d34e514c5..7547f8ee434 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -809,23 +809,30 @@ void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, /** * Convert between region relative coordinates (x,y) and depth component z and * a point in world space. */ -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]) +void ED_view3d_project_v3(const struct ARegion *region, const float world[3], float r_region_co[3]) { /* Viewport is set up to make coordinates relative to the region, not window. */ RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +} - GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +void ED_view3d_project_v2(const struct ARegion *region, const float world[3], float r_region_co[2]) +{ + /* Viewport is set up to make coordinates relative to the region, not window. */ + RegionView3D *rv3d = region->regiondata; + const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_2fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); } -bool ED_view3d_unproject( +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]) { RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; const float region_co[3] = {regionx, regiony, regionz}; - return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); + return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index f96c17d7cff..8ae5d4a29e9 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -299,8 +299,8 @@ void ED_view3d_clipping_calc( float xs = (ELEM(val, 0, 3)) ? rect->xmin : rect->xmax; float ys = (ELEM(val, 0, 1)) ? rect->ymin : rect->ymax; - ED_view3d_unproject(region, xs, ys, 0.0, bb->vec[val]); - ED_view3d_unproject(region, xs, ys, 1.0, bb->vec[4 + val]); + ED_view3d_unproject_v3(region, xs, ys, 0.0, bb->vec[val]); + ED_view3d_unproject_v3(region, xs, ys, 1.0, bb->vec[4 + val]); } /* optionally transform to object space */ @@ -1057,7 +1057,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - if (ED_view3d_unproject(region, centx, centy, depth_close, mouse_worldloc)) { + if (ED_view3d_unproject_v3(region, centx, centy, depth_close, mouse_worldloc)) { return true; } } @@ -1091,7 +1091,7 @@ bool ED_view3d_autodist_simple(ARegion *region, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, mouse_worldloc); + return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) @@ -1716,7 +1716,7 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, ED_view3d_depth_read_cached(depths, mval_ofs, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_ofs, depth, coords[i])) { + if (ED_view3d_depth_unproject_v3(region, mval_ofs, depth, coords[i])) { depths_valid[i] = true; } } @@ -1751,14 +1751,14 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, return false; } -bool ED_view3d_depth_unproject(const ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]) +bool ED_view3d_depth_unproject_v3(const ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]) { float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, r_location_world); + return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world); } void ED_view3d_depth_tag_update(RegionView3D *rv3d) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index ba8e5c1e2c6..17bfc23aea7 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -724,81 +724,92 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) static bool transform_event_modal_constraint(TransInfo *t, short modal_type) { - if (!(t->flag & T_NO_CONSTRAINT)) { - if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) { + if (t->flag & T_NO_CONSTRAINT) { + return false; + } + + if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) { + return false; + } + + int constraint_curr = -1; + + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { + t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE); + + /* Avoid changing orientation in this case. */ + constraint_curr = -2; + } + else if (t->con.mode & CON_APPLY) { + constraint_curr = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); + } + + int constraint_new; + const char *msg_2d = "", *msg_3d = ""; + + /* Initialize */ + switch (modal_type) { + case TFM_MODAL_AXIS_X: + msg_2d = TIP_("along X"); + msg_3d = TIP_("along %s X"); + constraint_new = CON_AXIS0; + break; + case TFM_MODAL_AXIS_Y: + msg_2d = TIP_("along Y"); + msg_3d = TIP_("along %s Y"); + constraint_new = CON_AXIS1; + break; + case TFM_MODAL_AXIS_Z: + msg_2d = TIP_("along Z"); + msg_3d = TIP_("along %s Z"); + constraint_new = CON_AXIS2; + break; + case TFM_MODAL_PLANE_X: + msg_3d = TIP_("locking %s X"); + constraint_new = CON_AXIS1 | CON_AXIS2; + break; + case TFM_MODAL_PLANE_Y: + msg_3d = TIP_("locking %s Y"); + constraint_new = CON_AXIS0 | CON_AXIS2; + break; + case TFM_MODAL_PLANE_Z: + msg_3d = TIP_("locking %s Z"); + constraint_new = CON_AXIS0 | CON_AXIS1; + break; + default: + /* Invalid key */ return false; - } - int constraint_curr = (t->con.mode & CON_APPLY) ? - t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2) : - -1; - int constraint_new; - const char *msg_2d = "", *msg_3d = ""; + } - /* Initialize */ - switch (modal_type) { - case TFM_MODAL_AXIS_X: - msg_2d = TIP_("along X"); - msg_3d = TIP_("along %s X"); - constraint_new = CON_AXIS0; - break; - case TFM_MODAL_AXIS_Y: - msg_2d = TIP_("along Y"); - msg_3d = TIP_("along %s Y"); - constraint_new = CON_AXIS1; - break; - case TFM_MODAL_AXIS_Z: - msg_2d = TIP_("along Z"); - msg_3d = TIP_("along %s Z"); - constraint_new = CON_AXIS2; - break; - case TFM_MODAL_PLANE_X: - msg_3d = TIP_("locking %s X"); - constraint_new = CON_AXIS1 | CON_AXIS2; - break; - case TFM_MODAL_PLANE_Y: - msg_3d = TIP_("locking %s Y"); - constraint_new = CON_AXIS0 | CON_AXIS2; - break; - case TFM_MODAL_PLANE_Z: - msg_3d = TIP_("locking %s Z"); - constraint_new = CON_AXIS0 | CON_AXIS1; - break; - default: - /* Invalid key */ - return false; + if (t->flag & T_2D_EDIT) { + BLI_assert(modal_type < TFM_MODAL_PLANE_X); + if (constraint_new == CON_AXIS2) { + return false; } - - if (t->flag & T_2D_EDIT) { - BLI_assert(modal_type < TFM_MODAL_PLANE_X); - if (constraint_new == CON_AXIS2) { - return false; - } - if (constraint_curr == constraint_new) { - stopConstraint(t); - } - else { - setUserConstraint(t, constraint_new, msg_2d); - } + if (constraint_curr == constraint_new) { + stopConstraint(t); } else { - short orient_index = 1; - if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { - /* Successive presses on existing axis, cycle orientation modes. */ - orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); - } + setUserConstraint(t, constraint_new, msg_2d); + } + } + else { + short orient_index = 1; + if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { + /* Successive presses on existing axis, cycle orientation modes. */ + orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); + } - transform_orientations_current_set(t, orient_index); - if (orient_index == 0) { - stopConstraint(t); - } - else { - setUserConstraint(t, constraint_new, msg_3d); - } + transform_orientations_current_set(t, orient_index); + if (orient_index == 0) { + stopConstraint(t); + } + else { + setUserConstraint(t, constraint_new, msg_3d); } - t->redraw |= TREDRAW_HARD; - return true; } - return false; + t->redraw |= TREDRAW_HARD; + return true; } int transformEvent(TransInfo *t, const wmEvent *event) @@ -814,7 +825,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } else if (event->type == MOUSEMOVE) { - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { t->con.mode |= CON_SELECT; } @@ -1062,10 +1073,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) t->state = TRANS_CONFIRM; } else if ((t->flag & T_NO_CONSTRAINT) == 0) { - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { /* Confirm. */ postSelectConstraint(t); - t->modifiers &= ~(MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE); + t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE); } else { if (t->options & CTX_CAMERA) { @@ -1079,8 +1090,9 @@ int transformEvent(TransInfo *t, const wmEvent *event) } } else { - t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? MOD_CONSTRAINT_SELECT : - MOD_CONSTRAINT_PLANE; + t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? + MOD_CONSTRAINT_SELECT_AXIS : + MOD_CONSTRAINT_SELECT_PLANE; if (t->con.mode & CON_APPLY) { stopConstraint(t); } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 0533c08bdc2..f0ced665679 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -46,6 +46,7 @@ * \{ */ struct ARegion; +struct BMPartialUpdate; struct Depsgraph; struct NumInput; struct Object; @@ -152,11 +153,11 @@ typedef enum { /** #TransInfo.modifiers */ typedef enum { - MOD_CONSTRAINT_SELECT = 1 << 0, + MOD_CONSTRAINT_SELECT_AXIS = 1 << 0, MOD_PRECISION = 1 << 1, MOD_SNAP = 1 << 2, MOD_SNAP_INVERT = 1 << 3, - MOD_CONSTRAINT_PLANE = 1 << 4, + MOD_CONSTRAINT_SELECT_PLANE = 1 << 4, } eTModifier; /** #TransSnap.status */ diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 615467932a7..8c74d5349ba 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -975,7 +975,6 @@ void initSelectConstraint(TransInfo *t) } setUserConstraint(t, CON_APPLY | CON_SELECT, "%s"); - setNearestAxis(t); } void selectConstraint(TransInfo *t) @@ -1062,7 +1061,7 @@ static void setNearestAxis3d(TransInfo *t) } if (len[0] <= len[1] && len[0] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS1 | CON_AXIS2); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename); } @@ -1072,7 +1071,7 @@ static void setNearestAxis3d(TransInfo *t) } } else if (len[1] <= len[0] && len[1] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS0 | CON_AXIS2); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename); } @@ -1082,7 +1081,7 @@ static void setNearestAxis3d(TransInfo *t) } } else if (len[2] <= len[1] && len[2] <= len[0]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS0 | CON_AXIS1); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename); } diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index f715228e25e..422370cb13b 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -52,6 +52,66 @@ /** \name Container TransCustomData Creation * \{ */ +static void tc_mesh_customdata_free_fn(struct TransInfo *t, + struct TransDataContainer *tc, + struct TransCustomData *custom_data); + +struct TransCustomDataLayer; +static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld); + +struct TransCustomDataMesh { + struct TransCustomDataLayer *cd_layer_correct; + struct { + struct BMPartialUpdate *cache; + + /** The size of proportional editing used for `partial_update_cache`. */ + float prop_size; + /** The size of proportional editing for the last update. */ + float prop_size_prev; + } partial_update; +}; + +static struct TransCustomDataMesh *tc_mesh_customdata_ensure(TransDataContainer *tc) +{ + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + BLI_assert(tc->custom.type.data == NULL || + tc->custom.type.free_cb == tc_mesh_customdata_free_fn); + if (tc->custom.type.data == NULL) { + tc->custom.type.data = MEM_callocN(sizeof(struct TransCustomDataMesh), __func__); + tc->custom.type.free_cb = tc_mesh_customdata_free_fn; + tcmd = tc->custom.type.data; + } + return tcmd; +} + +static void tc_mesh_customdata_free(struct TransCustomDataMesh *tcmd) +{ + if (tcmd->cd_layer_correct != NULL) { + tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct); + } + + if (tcmd->partial_update.cache != NULL) { + BM_mesh_partial_destroy(tcmd->partial_update.cache); + } + + MEM_freeN(tcmd); +} + +static void tc_mesh_customdata_free_fn(struct TransInfo *UNUSED(t), + struct TransDataContainer *UNUSED(tc), + struct TransCustomData *custom_data) +{ + struct TransCustomDataMesh *tcmd = custom_data->data; + tc_mesh_customdata_free(tcmd); + custom_data->data = NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData TransCustomDataLayer Creation + * \{ */ + struct TransCustomDataMergeGroup { /** map {BMVert: TransCustomDataLayerVert} */ struct LinkNode **cd_loop_groups; @@ -83,33 +143,6 @@ struct TransCustomDataLayer { bool use_merge_group; }; -static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t), - struct TransDataContainer *UNUSED(tc), - struct TransCustomData *custom_data) -{ - struct TransCustomDataLayer *tcld = custom_data->data; - bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - - if (tcld->bm_origfaces) { - BM_mesh_free(tcld->bm_origfaces); - } - if (tcld->origfaces) { - BLI_ghash_free(tcld->origfaces, NULL, NULL); - } - if (tcld->merge_group.origverts) { - BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); - } - if (tcld->arena) { - BLI_memarena_free(tcld->arena); - } - if (tcld->merge_group.customdatalayer_map) { - MEM_freeN(tcld->merge_group.customdatalayer_map); - } - - MEM_freeN(tcld); - custom_data->data = NULL; -} - #define USE_FACE_SUBSTITUTE #ifdef USE_FACE_SUBSTITUTE # define FACE_SUBSTITUTE_INDEX INT_MIN @@ -292,8 +325,8 @@ static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContai tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); } -static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc, - const bool use_merge_group) +static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create_impl( + TransDataContainer *tc, const bool use_merge_group) { BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; @@ -341,18 +374,41 @@ static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataCo return tcld; } -static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group) +static void tc_mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group) { struct TransCustomDataLayer *customdatacorrect; - customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group); + customdatacorrect = tc_mesh_customdatacorrect_create_impl(tc, use_merge_group); if (!customdatacorrect) { return; } - BLI_assert(tc->custom.type.data == NULL); - tc->custom.type.data = customdatacorrect; - tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn; + struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc); + BLI_assert(tcmd->cd_layer_correct == NULL); + tcmd->cd_layer_correct = customdatacorrect; +} + +static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld) +{ + bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + if (tcld->bm_origfaces) { + BM_mesh_free(tcld->bm_origfaces); + } + if (tcld->origfaces) { + BLI_ghash_free(tcld->origfaces, NULL, NULL); + } + if (tcld->merge_group.origverts) { + BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); + } + if (tcld->arena) { + BLI_memarena_free(tcld->arena); + } + if (tcld->merge_group.customdatalayer_map) { + MEM_freeN(tcld->merge_group.customdatalayer_map); + } + + MEM_freeN(tcld); } void transform_convert_mesh_customdatacorrect_init(TransInfo *t) @@ -390,10 +446,14 @@ void transform_convert_mesh_customdatacorrect_init(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->custom.type.data != NULL) { - tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type); + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + if (tcmd && tcmd->cd_layer_correct) { + tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct); + tcmd->cd_layer_correct = NULL; + } } - tc_mesh_customdata_create(tc, use_merge_group); + tc_mesh_customdatacorrect_create(tc, use_merge_group); } } @@ -555,10 +615,11 @@ static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tc static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final) { - if (!tc->custom.type.data) { + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL; + if (tcld == NULL) { return; } - struct TransCustomDataLayer *tcld = tc->custom.type.data; const bool use_merge_group = tcld->use_merge_group; struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; @@ -590,7 +651,8 @@ static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_fina static void tc_mesh_customdatacorrect_restore(struct TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - struct TransCustomDataLayer *tcld = tc->custom.type.data; + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL; if (!tcld) { continue; } @@ -1617,6 +1679,100 @@ void createTransEditVerts(TransInfo *t) /** \name Recalc Mesh Data * \{ */ +static bool bm_vert_tag_filter_fn(BMVert *v, void *UNUSED(user_data)) +{ + return BM_elem_flag_test(v, BM_ELEM_TAG); +} + +static BMPartialUpdate *tc_mesh_ensure_partial_update(TransInfo *t, TransDataContainer *tc) +{ + struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc); + + if (tcmd->partial_update.cache) { + + /* Recalculate partial update data when the proportional editing size changes. + * + * Note that decreasing the proportional editing size requires the existing + * partial data is used before recreating this partial data at the smaller size. + * Since excluding geometry from being transformed requires an update. + * + * Extra logic is needed to account for this situation. */ + + bool recalc; + if (tcmd->partial_update.prop_size_prev < t->prop_size) { + /* Size increase, simply recalculate. */ + recalc = true; + } + else if (tcmd->partial_update.prop_size_prev > t->prop_size) { + /* Size decreased, first use this partial data since reducing the size will transform + * geometry which needs recalculating. */ + tcmd->partial_update.prop_size_prev = t->prop_size; + recalc = false; + } + else if (tcmd->partial_update.prop_size != t->prop_size) { + BLI_assert(tcmd->partial_update.prop_size > tcmd->partial_update.prop_size_prev); + recalc = true; + } + else { + BLI_assert(t->prop_size == tcmd->partial_update.prop_size_prev); + recalc = false; + } + + if (!recalc) { + return tcmd->partial_update.cache; + } + + BM_mesh_partial_destroy(tcmd->partial_update.cache); + tcmd->partial_update.cache = NULL; + } + + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + int verts_len = 0; + int i; + TransData *td; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->factor != 0.0f) { + BMVert *v = (BMVert *)td->extra; + BM_elem_flag_enable(v, BM_ELEM_TAG); + verts_len += 1; + } + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co)); + + /* The equality check is to account for the case when topology mirror moves + * the vertex from it's original location to match it's symmetrical position, + * with proportional editing enabled. */ + if (BM_elem_flag_test(v_mirr, BM_ELEM_TAG) || !equals_v3v3(td_mirror->loc, td_mirror->iloc)) { + BMVert *v_mirr_other = (BMVert *)td_mirror->extra; + /* This assert should never fail since there is no overlap + * between mirrored vertices and non-mirrored. */ + BLI_assert(!BM_elem_flag_test(v_mirr_other, BM_ELEM_TAG)); + BM_elem_flag_enable(v_mirr_other, BM_ELEM_TAG); + verts_len += 1; + } + } + + tcmd->partial_update.cache = BM_mesh_partial_create_from_verts(em->bm, + &(BMPartialUpdate_Params){ + .do_tessellate = true, + .do_normals = true, + }, + verts_len, + bm_vert_tag_filter_fn, + NULL); + + tcmd->partial_update.prop_size_prev = t->prop_size; + tcmd->partial_update.prop_size = t->prop_size; + + return tcmd->partial_update.cache; +} + static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) { if (tc->use_mirror_axis_any) { @@ -1678,8 +1834,22 @@ void recalcData_mesh(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + + /* The additional cost of generating the partial connectivity data isn't justified + * when all data needs to be updated. + * + * While proportional editing can cause all geometry to need updating with a partial selection. + * It's impractical to calculate this ahead of time. + * Further, the down side of using partial updates when their not needed is negligible. */ + if (em->bm->totvert == em->bm->totvertsel) { + EDBM_mesh_normals_update(em); + BKE_editmesh_looptri_calc(em); + } + else { + BMPartialUpdate *partial_update_cache = tc_mesh_ensure_partial_update(t, tc); + BM_mesh_normals_update_with_partial(em->bm, partial_update_cache); + BKE_editmesh_looptri_calc_with_partial(em, partial_update_cache); + } } } /** \} */ diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index d61e7bb867c..350be247014 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -524,7 +524,7 @@ void constraintSizeLim(TransInfo *t, TransData *td) /** \name Transform (Rotation Utils) * \{ */ /* Used by Transform Rotation and Transform Normal Rotation */ -void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) +void headerRotation(TransInfo *t, char *str, const int str_size, float final) { size_t ofs = 0; @@ -533,25 +533,21 @@ void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Rotation: %s %s %s"), - &c[0], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen( + str + ofs, str_size - ofs, TIP_("Rotation: %s %s %s"), &c[0], t->con.text, t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Rotation: %.2f%s %s"), - RAD2DEGF(final), - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Rotation: %.2f%s %s"), + RAD2DEGF(final), + t->con.text, + t->proptext); } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( - str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf_rlen( + str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } @@ -811,7 +807,7 @@ void ElementRotation( /* -------------------------------------------------------------------- */ /** \name Transform (Resize Utils) * \{ */ -void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) +void headerResize(TransInfo *t, const float vec[3], char *str, const int str_size) { char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; @@ -827,59 +823,55 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Scale: %s%s %s"), - &tvec[0], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen( + str + ofs, str_size - ofs, TIP_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext); break; case 1: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Scale: %s : %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale: %s : %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + t->con.text, + t->proptext); break; case 2: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Scale: %s : %s : %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale: %s : %s : %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + t->con.text, + t->proptext); break; } } else { if (t->flag & T_2D_EDIT) { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Scale X: %s Y: %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale X: %s Y: %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + t->con.text, + t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Scale X: %s Y: %s Z: %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale X: %s Y: %s Z: %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + t->con.text, + t->proptext); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( - str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf_rlen( + str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 106dc68c9ee..a2b95eb3de4 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -47,7 +47,7 @@ void protectedTransBits(short protectflag, float vec[3]); void protectedSizeBits(short protectflag, float size[3]); void constraintTransLim(TransInfo *t, TransData *td); void constraintSizeLim(TransInfo *t, TransData *td); -void headerRotation(TransInfo *t, char *str, float final); +void headerRotation(TransInfo *t, char *str, int str_size, float final); void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData *td, @@ -55,7 +55,7 @@ void ElementRotation_ex(TransInfo *t, const float *center); void ElementRotation( TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around); -void headerResize(TransInfo *t, const float vec[3], char *str); +void headerResize(TransInfo *t, const float vec[3], char *str, int str_size); void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]); short getAnimEdit_SnapMode(TransInfo *t); void doAnimEdit_SnapFrame( diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index b7b3de69731..6f2bcc148ce 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -103,7 +103,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &angle); - headerRotation(t, str, angle); + headerRotation(t, str, sizeof(str), angle); axis_angle_normalized_to_mat3(mat, axis, angle); diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index 4330d5e79be..7e7b79c9f90 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -72,7 +72,7 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); const wmKeyMapItem *kmi = t->custom.mode.data; @@ -80,10 +80,10 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs); } - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_(" or Alt) Expand to fit %s"), - WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_(" or Alt) Expand to fit %s"), + WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); } static void applySeqSlideValue(TransInfo *t, const float val[2]) diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index 16c1c05a6f8..d255a7d5660 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1482,15 +1482,15 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs); } else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even)); if (use_even) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped)); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); /* done with header string */ diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 0d7d0be3c0e..1d7d1369f29 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -113,10 +113,10 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) pvec[j++] = t->values_final[i]; } } - headerResize(t, pvec, str); + headerResize(t, pvec, str, sizeof(str)); } else { - headerResize(t, t->values_final, str); + headerResize(t, t->values_final, str, sizeof(str)); } copy_m3_m3(t->mat, mat); /* used in gizmo */ diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 0fdbfb25989..8350e94e0e8 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -217,7 +217,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) t->values_final[0] = final; - headerRotation(t, str, final); + headerRotation(t, str, sizeof(str), final); const bool is_large_rotation = hasNumInput(&t->num); applyRotationValue(t, final, axis_final, is_large_rotation); diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index 6e497d85417..d2d73a14396 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -79,7 +79,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, unit); - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%s", c); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%s", c); } else { /* default header print */ @@ -93,12 +93,12 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) true); } else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f", distance); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f", distance); } } if (t->proptext[0]) { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, " %s", t->proptext); } ofs += BLI_strncpy_rlen(str + ofs, ", (", sizeof(str) - ofs); diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 77e57484bef..75ad83b0787 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -64,7 +64,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, t->values_final); - headerResize(t, t->values_final, str); + headerResize(t, t->values_final, str, sizeof(str)); FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c index 5ad6d04b4de..948242e547f 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.c +++ b/source/blender/editors/transform/transform_mode_timetranslate.c @@ -76,10 +76,10 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) } } - ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]); + ofs += BLI_snprintf_rlen(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]); if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c index 5a57a69f986..d05077ef1ef 100644 --- a/source/blender/editors/transform/transform_mode_trackball.c +++ b/source/blender/editors/transform/transform_mode_trackball.c @@ -102,24 +102,24 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, - sizeof(str) - ofs, - TIP_("Trackball: %s %s %s"), - &c[0], - &c[NUM_STR_REP_LEN], - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + sizeof(str) - ofs, + TIP_("Trackball: %s %s %s"), + &c[0], + &c[NUM_STR_REP_LEN], + t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - sizeof(str) - ofs, - TIP_("Trackball: %.2f %.2f %s"), - RAD2DEGF(phi[0]), - RAD2DEGF(phi[1]), - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + sizeof(str) - ofs, + TIP_("Trackball: %.2f %.2f %s"), + RAD2DEGF(phi[0]), + RAD2DEGF(phi[1]), + t->proptext); } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 0bef6364214..3088f6a7776 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -141,67 +141,67 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s (%s)%s %s %s", - &tvec[0], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "D: %s (%s)%s %s %s", + &tvec[0], + distvec, + t->con.text, + t->proptext, + autoik); break; case 1: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s D: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "D: %s D: %s (%s)%s %s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + distvec, + t->con.text, + t->proptext, + autoik); break; case 2: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s D: %s D: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "D: %s D: %s D: %s (%s)%s %s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + distvec, + t->con.text, + t->proptext, + autoik); break; } } else { if (t->flag & T_2D_EDIT) { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "Dx: %s Dy: %s (%s)%s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - distvec, - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "Dx: %s Dy: %s (%s)%s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + distvec, + t->con.text, + t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + distvec, + t->con.text, + t->proptext, + autoik); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } @@ -217,12 +217,12 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ WM_modalkeymap_items_to_string( t->keymap, TFM_MODAL_INSERTOFS_TOGGLE_DIR, true, str_km, sizeof(str_km)); - ofs += BLI_snprintf(str, - UI_MAX_DRAW_STR, - TIP_("Auto-offset set to %s - press %s to toggle direction | %s"), - str_dir, - str_km, - str_old); + ofs += BLI_snprintf_rlen(str, + UI_MAX_DRAW_STR, + TIP_("Auto-offset set to %s - press %s to toggle direction | %s"), + str_dir, + str_km, + str_old); MEM_freeN((void *)str_old); } @@ -471,6 +471,7 @@ void initTranslation(TransInfo *t) t->num.unit_type[2] = B_UNIT_NONE; } - transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL); + transform_mode_default_modal_orientation_set( + t, (t->options & CTX_CAMERA) ? V3D_ORIENT_VIEW : V3D_ORIENT_GLOBAL); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 1e5d027e253..e16aa636872 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -606,15 +606,15 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs); } else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even)); if (use_even) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped)); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); /* done with header string */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index e11341429a6..708f04bf044 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1471,7 +1471,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) if (EDBM_mesh_hide(em, swap)) { EDBM_update_generic(ob->data, true, false); } - return OPERATOR_FINISHED; + continue; } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { @@ -1609,7 +1609,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op) if (EDBM_mesh_reveal(em, select)) { EDBM_update_generic(ob->data, true, false); } - return OPERATOR_FINISHED; + continue; } if (use_face_center) { if (em->selectmode == SCE_SELECT_FACE) { diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 53c5def57e9..9c2c1621e23 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -34,8 +34,8 @@ MAKE_CPP_TYPE(int32, int32_t) MAKE_CPP_TYPE(uint32, uint32_t) MAKE_CPP_TYPE(uint8, uint8_t) -MAKE_CPP_TYPE(Color4f, blender::Color4f) -MAKE_CPP_TYPE(Color4b, blender::Color4b) +MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f) +MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b) MAKE_CPP_TYPE(string, std::string) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index ea37558fa7f..9f03e493ea8 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -204,8 +204,6 @@ static void deformStroke(GpencilModifierData *md, /* Fill using opacity factor. */ if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { - gps->fill_opacity_fac = mmd->factor; - float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true); gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 512e3af063a..126949cd659 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -170,8 +170,10 @@ static void deformStroke(GpencilModifierData *md, weight *= curvef; } - float fac_begin = mmd->flag & GP_THICK_NORMALIZE ? 1 : mmd->thickness_fac; - target *= interpf(fac_begin, mmd->fading_end_factor, factor_depth); + /* Apply distance fading. */ + if (mmd->flag & GP_THICK_FADING) { + target = interpf(target, mmd->fading_end_factor, factor_depth); + } pt->pressure = interpf(target, pt->pressure, weight); diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 44ff0616fe9..712e92d017d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -93,8 +93,8 @@ typedef struct LineartElementLinkNode { float crease_threshold; } LineartElementLinkNode; -typedef struct LineartLineSegment { - struct LineartLineSegment *next, *prev; +typedef struct LineartEdgeSegment { + struct LineartEdgeSegment *next, *prev; /** at==0: left at==1: right (this is in 2D projected space) */ double at; /** Occlusion level after "at" point */ @@ -107,7 +107,7 @@ typedef struct LineartLineSegment { * enough for most cases. */ unsigned char transparency_mask; -} LineartLineSegment; +} LineartEdgeSegment; typedef struct LineartVert { double gloc[3]; @@ -155,7 +155,7 @@ typedef struct LineartEdge { /** * Still need this entry because culled lines will not add to object - * #LineartElementLinkNode node (known as `reln` internally). + * #LineartElementLinkNode node (known as `eln` internally). * * TODO: If really need more savings, we can allocate this in a "extended" way too, but we need * another bit in flags to be able to show the difference. @@ -163,8 +163,8 @@ typedef struct LineartEdge { struct Object *object_ref; } LineartEdge; -typedef struct LineartLineChain { - struct LineartLineChain *next, *prev; +typedef struct LineartEdgeChain { + struct LineartEdgeChain *next, *prev; ListBase chain; /** Calculated before draw command. */ @@ -179,10 +179,10 @@ typedef struct LineartLineChain { unsigned char transparency_mask; struct Object *object_ref; -} LineartLineChain; +} LineartEdgeChain; -typedef struct LineartLineChainItem { - struct LineartLineChainItem *next, *prev; +typedef struct LineartEdgeChainItem { + struct LineartEdgeChainItem *next, *prev; /** Need z value for fading */ float pos[3]; /** For restoring position to 3d space */ @@ -192,12 +192,12 @@ typedef struct LineartLineChainItem { char occlusion; unsigned char transparency_mask; size_t index; -} LineartLineChainItem; +} LineartEdgeChainItem; typedef struct LineartChainRegisterEntry { struct LineartChainRegisterEntry *next, *prev; - LineartLineChain *rlc; - LineartLineChainItem *rlci; + LineartEdgeChain *ec; + LineartEdgeChainItem *eci; char picked; /* left/right mark. @@ -205,6 +205,17 @@ typedef struct LineartChainRegisterEntry { char is_left; } LineartChainRegisterEntry; +enum eLineArtTileRecursiveLimit { + /* If tile gets this small, it's already much smaller than a pixel. No need to continue + * splitting. */ + LRT_TILE_RECURSIVE_PERSPECTIVE = 30, + /* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */ + LRT_TILE_RECURSIVE_ORTHO = 10, +}; + +#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100 +#define LRT_TILE_EDGE_COUNT_INITIAL 32 + typedef struct LineartRenderBuffer { struct LineartRenderBuffer *prev, *next; @@ -219,6 +230,11 @@ typedef struct LineartRenderBuffer { struct LineartBoundingArea *initial_bounding_areas; unsigned int bounding_area_count; + /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there + * will be a lot of triangles aligned in line which can not be separated by continue subdividing + * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */ + int tile_recursive_level; + ListBase vertex_buffer_pointers; ListBase line_buffer_pointers; ListBase triangle_buffer_pointers; @@ -237,31 +253,14 @@ typedef struct LineartRenderBuffer { int triangle_size; - unsigned int contour_count; - unsigned int contour_processed; - LineartEdge *contour_managed; - /** A single linked list (cast to #LinkNode). */ - LineartEdge *contours; - - unsigned int intersection_count; - unsigned int intersection_processed; - LineartEdge *intersection_managed; - LineartEdge *intersection_lines; - - unsigned int crease_count; - unsigned int crease_processed; - LineartEdge *crease_managed; - LineartEdge *crease_lines; - - unsigned int material_line_count; - unsigned int material_processed; - LineartEdge *material_managed; - LineartEdge *material_lines; - - unsigned int edge_mark_count; - unsigned int edge_mark_processed; - LineartEdge *edge_mark_managed; - LineartEdge *edge_marks; + /* Although using ListBase here, LineartEdge is single linked list. + * list.last is used to store worker progress along the list. + * See lineart_main_occlusion_begin() for more info. */ + ListBase contour; + ListBase intersection; + ListBase crease; + ListBase material; + ListBase edge_mark; ListBase chains; @@ -334,20 +333,13 @@ typedef struct LineartRenderTaskInfo { int thread_id; - LineartEdge *contour; - LineartEdge *contour_end; - - LineartEdge *intersection; - LineartEdge *intersection_end; - - LineartEdge *crease; - LineartEdge *crease_end; - - LineartEdge *material; - LineartEdge *material_end; - - LineartEdge *edge_mark; - LineartEdge *edge_mark_end; + /* These lists only denote the part of the main edge list that the thread should iterate over. + * Be careful to not iterate outside of these bounds as it is not thread safe to do so. */ + ListBase contour; + ListBase intersection; + ListBase crease; + ListBase material; + ListBase edge_mark; } LineartRenderTaskInfo; @@ -387,10 +379,14 @@ typedef struct LineartBoundingArea { ListBase up; ListBase bp; - short triangle_count; + int16_t triangle_count; + int16_t max_triangle_count; + int16_t line_count; + int16_t max_line_count; - ListBase linked_triangles; - ListBase linked_lines; + /* Use array for speeding up multiple accesses. */ + struct LineartTriangle **linked_triangles; + struct LineartEdge **linked_lines; /** Reserved for image space reduction && multi-thread chaining. */ ListBase linked_chains; @@ -529,7 +525,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); -int MOD_lineart_chain_count(const LineartLineChain *rlc); +int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, @@ -565,6 +561,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, const char *vgname, int modifier_flags); -float MOD_lineart_chain_compute_length(LineartLineChain *rlc); +float MOD_lineart_chain_compute_length(LineartEdgeChain *ec); void ED_operatortypes_lineart(void); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 4ad8eed6ddd..23928b4ccda 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -31,17 +31,17 @@ #include <math.h> -#define LRT_OTHER_RV(e, rv) ((rv) == (e)->v1 ? (e)->v2 : ((rv) == (e)->v2 ? (e)->v1 : NULL)) +#define LRT_OTHER_VERT(e, vt) ((vt) == (e)->v1 ? (e)->v2 : ((vt) == (e)->v2 ? (e)->v1 : NULL)) /* Get a connected line, only for lines who has the exact given vert, or (in the case of * intersection lines) who has a vert that has the exact same position. */ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, - LineartVert *rv, - LineartVert **new_rv, + LineartVert *vt, + LineartVert **new_vt, int match_flag) { - LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) { - LineartEdge *n_e = lip->data; + for (int i = 0; i < ba->line_count; i++) { + LineartEdge *n_e = ba->linked_lines[i]; if ((!(n_e->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (n_e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) { continue; @@ -51,18 +51,18 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, continue; } - *new_rv = LRT_OTHER_RV(n_e, rv); - if (*new_rv) { + *new_vt = LRT_OTHER_VERT(n_e, vt); + if (*new_vt) { return n_e; } if (n_e->flags & LRT_EDGE_FLAG_INTERSECTION) { - if (rv->fbcoord[0] == n_e->v1->fbcoord[0] && rv->fbcoord[1] == n_e->v1->fbcoord[1]) { - *new_rv = LRT_OTHER_RV(n_e, n_e->v1); + if (vt->fbcoord[0] == n_e->v1->fbcoord[0] && vt->fbcoord[1] == n_e->v1->fbcoord[1]) { + *new_vt = LRT_OTHER_VERT(n_e, n_e->v1); return n_e; } - if (rv->fbcoord[0] == n_e->v2->fbcoord[0] && rv->fbcoord[1] == n_e->v2->fbcoord[1]) { - *new_rv = LRT_OTHER_RV(n_e, n_e->v2); + if (vt->fbcoord[0] == n_e->v2->fbcoord[0] && vt->fbcoord[1] == n_e->v2->fbcoord[1]) { + *new_vt = LRT_OTHER_VERT(n_e, n_e->v2); return n_e; } } @@ -71,33 +71,33 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, return NULL; } -static LineartLineChain *lineart_chain_create(LineartRenderBuffer *rb) +static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb) { - LineartLineChain *rlc; - rlc = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChain)); + LineartEdgeChain *ec; + ec = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChain)); - BLI_addtail(&rb->chains, rlc); + BLI_addtail(&rb->chains, ec); - return rlc; + return ec; } -static bool lineart_point_overlapping(LineartLineChainItem *rlci, +static bool lineart_point_overlapping(LineartEdgeChainItem *eci, float x, float y, double threshold) { - if (!rlci) { + if (!eci) { return false; } - if (((rlci->pos[0] + threshold) >= x) && ((rlci->pos[0] - threshold) <= x) && - ((rlci->pos[1] + threshold) >= y) && ((rlci->pos[1] - threshold) <= y)) { + if (((eci->pos[0] + threshold) >= x) && ((eci->pos[0] - threshold) <= x) && + ((eci->pos[1] + threshold) >= y) && ((eci->pos[1] - threshold) <= y)) { return true; } return false; } -static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, - LineartLineChain *rlc, +static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, + LineartEdgeChain *ec, float *fbcoord, float *gpos, float *normal, @@ -106,35 +106,35 @@ static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, unsigned char transparency_mask, size_t index) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; - if (lineart_point_overlapping(rlc->chain.last, fbcoord[0], fbcoord[1], 1e-5)) { + if (lineart_point_overlapping(ec->chain.last, fbcoord[0], fbcoord[1], 1e-5)) { /* Because the new chain point is overlapping, just replace the type and occlusion level of the * current point. This makes it so that the line to the point after this one has the correct * type and level. */ - LineartLineChainItem *old_rlci = rlc->chain.last; + LineartEdgeChainItem *old_rlci = ec->chain.last; old_rlci->line_type = type; old_rlci->occlusion = level; old_rlci->transparency_mask = transparency_mask; return old_rlci; } - rlci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChainItem)); + eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(rlci->pos, fbcoord); - copy_v3_v3(rlci->gpos, gpos); - rlci->index = index; - copy_v3_v3(rlci->normal, normal); - rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; - rlci->occlusion = level; - rlci->transparency_mask = transparency_mask; - BLI_addtail(&rlc->chain, rlci); + copy_v2_v2(eci->pos, fbcoord); + copy_v3_v3(eci->gpos, gpos); + eci->index = index; + copy_v3_v3(eci->normal, normal); + eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; + eci->occlusion = level; + eci->transparency_mask = transparency_mask; + BLI_addtail(&ec->chain, eci); - return rlci; + return eci; } -static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb, - LineartLineChain *rlc, +static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb, + LineartEdgeChain *ec, float *fbcoord, float *gpos, float *normal, @@ -143,32 +143,32 @@ static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb unsigned char transparency_mask, size_t index) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; - if (lineart_point_overlapping(rlc->chain.first, fbcoord[0], fbcoord[1], 1e-5)) { - return rlc->chain.first; + if (lineart_point_overlapping(ec->chain.first, fbcoord[0], fbcoord[1], 1e-5)) { + return ec->chain.first; } - rlci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChainItem)); + eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(rlci->pos, fbcoord); - copy_v3_v3(rlci->gpos, gpos); - rlci->index = index; - copy_v3_v3(rlci->normal, normal); - rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; - rlci->occlusion = level; - rlci->transparency_mask = transparency_mask; - BLI_addhead(&rlc->chain, rlci); + copy_v2_v2(eci->pos, fbcoord); + copy_v3_v3(eci->gpos, gpos); + eci->index = index; + copy_v3_v3(eci->normal, normal); + eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; + eci->occlusion = level; + eci->transparency_mask = transparency_mask; + BLI_addhead(&ec->chain, eci); - return rlci; + return eci; } void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) { - LineartLineChain *rlc; - LineartLineChainItem *rlci; + LineartEdgeChain *ec; + LineartEdgeChainItem *eci; LineartBoundingArea *ba; - LineartLineSegment *rls; + LineartEdgeSegment *es; int last_occlusion; unsigned char last_transparency; /* Used when converting from double. */ @@ -192,14 +192,14 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; - rlc = lineart_chain_create(rb); + ec = lineart_chain_create(rb); /* One chain can only have one object_ref, * so we assign it based on the first segment we found. */ - rlc->object_ref = e->object_ref; + ec->object_ref = e->object_ref; LineartEdge *new_e = e; - LineartVert *new_rv; + LineartVert *new_vt; float N[3] = {0}; if (e->t1) { @@ -218,19 +218,19 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) /* Step 1: grow left. */ ba = MOD_lineart_get_bounding_area(rb, e->v1->fbcoord[0], e->v1->fbcoord[1]); - new_rv = e->v1; - rls = e->segments.first; - VERT_COORD_TO_FLOAT(new_rv); + new_vt = e->v1; + es = e->segments.first; + VERT_COORD_TO_FLOAT(new_vt); lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, e->v1_obindex); - while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) { + while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) { new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; if (new_e->t1 || new_e->t2) { @@ -248,41 +248,41 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) normalize_v3(N); } - if (new_rv == new_e->v1) { - for (rls = new_e->segments.last; rls; rls = rls->prev) { + if (new_vt == new_e->v1) { + for (es = new_e->segments.last; es; es = es->prev) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, new_e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, new_e->v1_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } } - else if (new_rv == new_e->v2) { - rls = new_e->segments.first; - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; - rls = rls->next; - for (; rls; rls = rls->next) { + else if (new_vt == new_e->v2) { + es = new_e->segments.first; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; + es = es->next; + for (; es; es = es->next) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -290,12 +290,12 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_occlusion, last_transparency, new_e->v2_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } VERT_COORD_TO_FLOAT(new_e->v2); lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -304,7 +304,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency, new_e->v2_obindex); } - ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } /* Restore normal value. */ @@ -324,31 +324,31 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) } /* Step 2: Adding all cuts from the given line, so we can continue connecting the right side * of the line. */ - rls = e->segments.first; - last_occlusion = ((LineartLineSegment *)rls)->occlusion; - last_transparency = ((LineartLineSegment *)rls)->transparency_mask; - for (rls = rls->next; rls; rls = rls->next) { + es = e->segments.first; + last_occlusion = ((LineartEdgeSegment *)es)->occlusion; + last_transparency = ((LineartEdgeSegment *)es)->transparency_mask; + for (es = es->next; es; es = es->next) { double gpos[3], lpos[3]; double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, e->v1_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } VERT_COORD_TO_FLOAT(e->v2) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -359,8 +359,8 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) /* Step 3: grow right. */ ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]); - new_rv = e->v2; - while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) { + new_vt = e->v2; + while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) { new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; if (new_e->t1 || new_e->t2) { @@ -379,27 +379,27 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) } /* Fix leading vertex type. */ - rlci = rlc->chain.last; - rlci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE; + eci = ec->chain.last; + eci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE; - if (new_rv == new_e->v1) { - rls = new_e->segments.last; - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + if (new_vt == new_e->v1) { + es = new_e->segments.last; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; /* Fix leading vertex occlusion. */ - rlci->occlusion = last_occlusion; - rlci->transparency_mask = last_transparency; - for (rls = new_e->segments.last; rls; rls = rls->prev) { + eci->occlusion = last_occlusion; + eci->transparency_mask = last_transparency; + for (es = new_e->segments.last; es; es = es->prev) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); - last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion; - last_transparency = rls->prev ? rls->prev->transparency_mask : last_transparency; + last_occlusion = es->prev ? es->prev->occlusion : last_occlusion; + last_transparency = es->prev ? es->prev->transparency_mask : last_transparency; POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -409,35 +409,35 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->v1_obindex); } } - else if (new_rv == new_e->v2) { - rls = new_e->segments.first; - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; - rlci->occlusion = last_occlusion; - rlci->transparency_mask = last_transparency; - rls = rls->next; - for (; rls; rls = rls->next) { + else if (new_vt == new_e->v2) { + es = new_e->segments.first; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; + eci->occlusion = last_occlusion; + eci->transparency_mask = last_transparency; + es = es->next; + for (; es; es = es->next) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, new_e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, new_e->v2_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } VERT_COORD_TO_FLOAT(new_e->v2) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -446,57 +446,57 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency, new_e->v2_obindex); } - ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } if (rb->fuzzy_everything) { - rlc->type = LRT_EDGE_FLAG_CONTOUR; + ec->type = LRT_EDGE_FLAG_CONTOUR; } else { - rlc->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE); + ec->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE); } } LRT_ITER_ALL_LINES_END } -static LineartBoundingArea *lineart_bounding_area_get_rlci_recursive(LineartRenderBuffer *rb, - LineartBoundingArea *root, - LineartLineChainItem *rlci) +static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartRenderBuffer *rb, + LineartBoundingArea *root, + LineartEdgeChainItem *eci) { if (root->child == NULL) { return root; } LineartBoundingArea *ch = root->child; -#define IN_BOUND(ba, rlci) \ - ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1] +#define IN_BOUND(ba, eci) \ + ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1] - if (IN_BOUND(ch[0], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[0], rlci); + if (IN_BOUND(ch[0], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[0], eci); } - if (IN_BOUND(ch[1], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci); + if (IN_BOUND(ch[1], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[1], eci); } - if (IN_BOUND(ch[2], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci); + if (IN_BOUND(ch[2], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[2], eci); } - if (IN_BOUND(ch[3], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[3], rlci); + if (IN_BOUND(ch[3], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[3], eci); } #undef IN_BOUND return NULL; } static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb, - LineartLineChainItem *rlci) + LineartEdgeChainItem *eci) { - if (!rlci) { + if (!eci) { return NULL; } - LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, rlci->pos[0], rlci->pos[1]); + LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, eci->pos[0], eci->pos[1]); if (root == NULL) { return NULL; } - return lineart_bounding_area_get_rlci_recursive(rb, root, rlci); + return lineart_bounding_area_get_eci_recursive(rb, root, eci); } /** @@ -507,61 +507,61 @@ static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuf */ static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb, LineartBoundingArea *root, - LineartLineChain *rlc, - LineartLineChainItem *rlci) + LineartEdgeChain *ec, + LineartEdgeChainItem *eci) { if (root->child == NULL) { LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized( - &root->linked_chains, &rb->render_data_pool, rlc, sizeof(LineartChainRegisterEntry)); + &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry)); - cre->rlci = rlci; + cre->eci = eci; - if (rlci == rlc->chain.first) { + if (eci == ec->chain.first) { cre->is_left = 1; } } else { LineartBoundingArea *ch = root->child; -#define IN_BOUND(ba, rlci) \ - ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1] +#define IN_BOUND(ba, eci) \ + ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1] - if (IN_BOUND(ch[0], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[0], rlc, rlci); + if (IN_BOUND(ch[0], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[0], ec, eci); } - else if (IN_BOUND(ch[1], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[1], rlc, rlci); + else if (IN_BOUND(ch[1], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[1], ec, eci); } - else if (IN_BOUND(ch[2], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[2], rlc, rlci); + else if (IN_BOUND(ch[2], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[2], ec, eci); } - else if (IN_BOUND(ch[3], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[3], rlc, rlci); + else if (IN_BOUND(ch[3], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[3], ec, eci); } #undef IN_BOUND } } -static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartLineChain *rlc) +static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdgeChain *ec) { - LineartLineChainItem *pl = rlc->chain.first; - LineartLineChainItem *pr = rlc->chain.last; + LineartEdgeChainItem *pl = ec->chain.first; + LineartEdgeChainItem *pr = ec->chain.last; LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]); LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(rb, pr->pos[0], pr->pos[1]); if (ba1) { - lineart_bounding_area_link_point_recursive(rb, ba1, rlc, pl); + lineart_bounding_area_link_point_recursive(rb, ba1, ec, pl); } if (ba2) { - lineart_bounding_area_link_point_recursive(rb, ba2, rlc, pr); + lineart_bounding_area_link_point_recursive(rb, ba2, ec, pr); } } void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) { - LineartLineChain *rlc, *new_rlc; - LineartLineChainItem *rlci, *next_rlci; + LineartEdgeChain *ec, *new_rlc; + LineartEdgeChainItem *eci, *next_rlci; ListBase swap = {0}; swap.first = rb->chains.first; @@ -569,59 +569,59 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) rb->chains.last = rb->chains.first = NULL; - while ((rlc = BLI_pophead(&swap)) != NULL) { - rlc->next = rlc->prev = NULL; - BLI_addtail(&rb->chains, rlc); - LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first; + while ((ec = BLI_pophead(&swap)) != NULL) { + ec->next = ec->prev = NULL; + BLI_addtail(&rb->chains, ec); + LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first; int fixed_occ = first_rlci->occlusion; unsigned char fixed_mask = first_rlci->transparency_mask; - rlc->level = fixed_occ; - rlc->transparency_mask = fixed_mask; - for (rlci = first_rlci->next; rlci; rlci = next_rlci) { - next_rlci = rlci->next; - if (rlci->occlusion != fixed_occ || rlci->transparency_mask != fixed_mask) { + ec->level = fixed_occ; + ec->transparency_mask = fixed_mask; + for (eci = first_rlci->next; eci; eci = next_rlci) { + next_rlci = eci->next; + if (eci->occlusion != fixed_occ || eci->transparency_mask != fixed_mask) { if (next_rlci) { - if (lineart_point_overlapping(next_rlci, rlci->pos[0], rlci->pos[1], 1e-5)) { + if (lineart_point_overlapping(next_rlci, eci->pos[0], eci->pos[1], 1e-5)) { continue; } } else { /* Set the same occlusion level for the end vertex, so when further connection is needed * the backwards occlusion info is also correct. */ - rlci->occlusion = fixed_occ; - rlci->transparency_mask = fixed_mask; + eci->occlusion = fixed_occ; + eci->transparency_mask = fixed_mask; /* No need to split at the last point anyway. */ break; } new_rlc = lineart_chain_create(rb); - new_rlc->chain.first = rlci; - new_rlc->chain.last = rlc->chain.last; - rlc->chain.last = rlci->prev; - ((LineartLineChainItem *)rlc->chain.last)->next = 0; - rlci->prev = 0; + new_rlc->chain.first = eci; + new_rlc->chain.last = ec->chain.last; + ec->chain.last = eci->prev; + ((LineartEdgeChainItem *)ec->chain.last)->next = 0; + eci->prev = 0; /* End the previous one. */ lineart_chain_append_point(rb, - rlc, - rlci->pos, - rlci->gpos, - rlci->normal, - rlci->line_type, + ec, + eci->pos, + eci->gpos, + eci->normal, + eci->line_type, fixed_occ, fixed_mask, - rlci->index); - new_rlc->object_ref = rlc->object_ref; - new_rlc->type = rlc->type; - rlc = new_rlc; - fixed_occ = rlci->occlusion; - fixed_mask = rlci->transparency_mask; - rlc->level = fixed_occ; - rlc->transparency_mask = fixed_mask; + eci->index); + new_rlc->object_ref = ec->object_ref; + new_rlc->type = ec->type; + ec = new_rlc; + fixed_occ = eci->occlusion; + fixed_mask = eci->transparency_mask; + ec->level = fixed_occ; + ec->transparency_mask = fixed_mask; } } } - LISTBASE_FOREACH (LineartLineChain *, irlc, &rb->chains) { - lineart_bounding_area_link_chain(rb, irlc); + LISTBASE_FOREACH (LineartEdgeChain *, iec, &rb->chains) { + lineart_bounding_area_link_chain(rb, iec); } } @@ -629,12 +629,12 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) * Note: segment type (crease/material/contour...) is ambiguous after this. */ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb), - LineartLineChain *onto, - LineartLineChain *sub, + LineartEdgeChain *onto, + LineartEdgeChain *sub, int reverse_1, int reverse_2) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; if (onto->type == LRT_EDGE_FLAG_INTERSECTION) { if (sub->object_ref) { onto->object_ref = sub->object_ref; @@ -650,38 +650,38 @@ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb), if (reverse_2) { /* L--R R-L. */ BLI_listbase_reverse(&sub->chain); } - rlci = sub->chain.first; - if (lineart_point_overlapping(onto->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) { + eci = sub->chain.first; + if (lineart_point_overlapping(onto->chain.last, eci->pos[0], eci->pos[1], 1e-5)) { BLI_pophead(&sub->chain); if (sub->chain.first == NULL) { return; } } - ((LineartLineChainItem *)onto->chain.last)->next = sub->chain.first; - ((LineartLineChainItem *)sub->chain.first)->prev = onto->chain.last; + ((LineartEdgeChainItem *)onto->chain.last)->next = sub->chain.first; + ((LineartEdgeChainItem *)sub->chain.first)->prev = onto->chain.last; onto->chain.last = sub->chain.last; } else { /* L-R L--R. */ if (!reverse_2) { /* R-L L--R. */ BLI_listbase_reverse(&sub->chain); } - rlci = onto->chain.first; - if (lineart_point_overlapping(sub->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) { + eci = onto->chain.first; + if (lineart_point_overlapping(sub->chain.last, eci->pos[0], eci->pos[1], 1e-5)) { BLI_pophead(&onto->chain); if (onto->chain.first == NULL) { return; } } - ((LineartLineChainItem *)sub->chain.last)->next = onto->chain.first; - ((LineartLineChainItem *)onto->chain.first)->prev = sub->chain.last; + ((LineartEdgeChainItem *)sub->chain.last)->next = onto->chain.first; + ((LineartEdgeChainItem *)onto->chain.first)->prev = sub->chain.last; onto->chain.first = sub->chain.first; } } static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb, LineartBoundingArea *ba, - LineartLineChain *rlc, - LineartLineChainItem *rlci, + LineartEdgeChain *ec, + LineartEdgeChainItem *eci, int occlusion, unsigned char transparency_mask, float dist, @@ -694,12 +694,12 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf /* Keep using for loop because `cre` could be removed from the iteration before getting to the * next one. */ LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) { - if (cre->rlc->object_ref != rlc->object_ref) { + if (cre->ec->object_ref != ec->object_ref) { if (!rb->fuzzy_everything) { if (rb->fuzzy_intersections) { /* If none of those are intersection lines... */ - if ((!(cre->rlc->type & LRT_EDGE_FLAG_INTERSECTION)) && - (!(rlc->type & LRT_EDGE_FLAG_INTERSECTION))) { + if ((!(cre->ec->type & LRT_EDGE_FLAG_INTERSECTION)) && + (!(ec->type & LRT_EDGE_FLAG_INTERSECTION))) { continue; /* We don't want to chain along different objects at the moment. */ } } @@ -708,18 +708,18 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } } } - if (cre->rlc->picked || cre->picked) { + if (cre->ec->picked || cre->picked) { continue; } - if (cre->rlc == rlc || (!cre->rlc->chain.first) || (cre->rlc->level != occlusion) || - (cre->rlc->transparency_mask != transparency_mask)) { + if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) || + (cre->ec->transparency_mask != transparency_mask)) { continue; } if (!rb->fuzzy_everything) { - if (cre->rlc->type != rlc->type) { + if (cre->ec->type != ec->type) { if (rb->fuzzy_intersections) { - if (!(cre->rlc->type == LRT_EDGE_FLAG_INTERSECTION || - rlc->type == LRT_EDGE_FLAG_INTERSECTION)) { + if (!(cre->ec->type == LRT_EDGE_FLAG_INTERSECTION || + ec->type == LRT_EDGE_FLAG_INTERSECTION)) { continue; /* Fuzzy intersections but no intersection line found. */ } } @@ -729,7 +729,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } } - float new_len = len_v2v2(cre->rlci->pos, rlci->pos); + float new_len = len_v2v2(cre->eci->pos, eci->pos); if (new_len < dist) { closest_cre = cre; dist = new_len; @@ -748,7 +748,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf LISTBASE_FOREACH (LinkData *, ld, list) { \ LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \ adjacent_closest = lineart_chain_get_closest_cre( \ - rb, sba, rlc, rlci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \ + rb, sba, ec, eci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \ if (adjacent_new_len < dist) { \ dist = adjacent_new_len; \ closest_cre = adjacent_closest; \ @@ -756,10 +756,10 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } \ } if (!caller_ba) { - LRT_TEST_ADJACENT_AREAS(rlci->pos[0] - ba->l, &ba->lp); - LRT_TEST_ADJACENT_AREAS(ba->r - rlci->pos[0], &ba->rp); - LRT_TEST_ADJACENT_AREAS(ba->u - rlci->pos[1], &ba->up); - LRT_TEST_ADJACENT_AREAS(rlci->pos[1] - ba->b, &ba->bp); + LRT_TEST_ADJACENT_AREAS(eci->pos[0] - ba->l, &ba->lp); + LRT_TEST_ADJACENT_AREAS(ba->r - eci->pos[0], &ba->rp); + LRT_TEST_ADJACENT_AREAS(ba->u - eci->pos[1], &ba->up); + LRT_TEST_ADJACENT_AREAS(eci->pos[1] - ba->b, &ba->bp); } if (result_new_len) { (*result_new_len) = dist; @@ -774,8 +774,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf */ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) { - LineartLineChain *rlc; - LineartLineChainItem *rlci_l, *rlci_r; + LineartEdgeChain *ec; + LineartEdgeChainItem *rlci_l, *rlci_r; LineartBoundingArea *ba_l, *ba_r; LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre; float dist = rb->chaining_image_threshold; @@ -793,24 +793,24 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) rb->chains.last = rb->chains.first = NULL; - while ((rlc = BLI_pophead(&swap)) != NULL) { - rlc->next = rlc->prev = NULL; - if (rlc->picked) { + while ((ec = BLI_pophead(&swap)) != NULL) { + ec->next = ec->prev = NULL; + if (ec->picked) { continue; } - BLI_addtail(&rb->chains, rlc); + BLI_addtail(&rb->chains, ec); - occlusion = rlc->level; - transparency_mask = rlc->transparency_mask; + occlusion = ec->level; + transparency_mask = ec->transparency_mask; - rlci_l = rlc->chain.first; - rlci_r = rlc->chain.last; + rlci_l = ec->chain.first; + rlci_r = ec->chain.last; while ((ba_l = lineart_bounding_area_get_end_point(rb, rlci_l)) && (ba_r = lineart_bounding_area_get_end_point(rb, rlci_r))) { closest_cre_l = lineart_chain_get_closest_cre( - rb, ba_l, rlc, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL); + rb, ba_l, ec, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL); closest_cre_r = lineart_chain_get_closest_cre( - rb, ba_r, rlc, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL); + rb, ba_r, ec, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL); if (closest_cre_l && closest_cre_r) { if (dist_l < dist_r) { closest_cre = closest_cre_l; @@ -834,56 +834,56 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) break; } closest_cre->picked = 1; - closest_cre->rlc->picked = 1; + closest_cre->ec->picked = 1; if (closest_cre->is_left) { - lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 0); + lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 0); } else { - lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 1); + lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 1); } - BLI_remlink(&swap, closest_cre->rlc); - rlci_l = rlc->chain.first; - rlci_r = rlc->chain.last; + BLI_remlink(&swap, closest_cre->ec); + rlci_l = ec->chain.first; + rlci_r = ec->chain.last; } - rlc->picked = 1; + ec->picked = 1; } } /** * Length is in image space. */ -float MOD_lineart_chain_compute_length(LineartLineChain *rlc) +float MOD_lineart_chain_compute_length(LineartEdgeChain *ec) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; float offset_accum = 0; float dist; float last_point[2]; - rlci = rlc->chain.first; - copy_v2_v2(last_point, rlci->pos); - for (rlci = rlc->chain.first; rlci; rlci = rlci->next) { - dist = len_v2v2(rlci->pos, last_point); + eci = ec->chain.first; + copy_v2_v2(last_point, eci->pos); + for (eci = ec->chain.first; eci; eci = eci->next) { + dist = len_v2v2(eci->pos, last_point); offset_accum += dist; - copy_v2_v2(last_point, rlci->pos); + copy_v2_v2(last_point, eci->pos); } return offset_accum; } void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold) { - LineartLineChain *rlc, *next_rlc; - for (rlc = rb->chains.first; rlc; rlc = next_rlc) { - next_rlc = rlc->next; - if (MOD_lineart_chain_compute_length(rlc) < threshold) { - BLI_remlink(&rb->chains, rlc); + LineartEdgeChain *ec, *next_rlc; + for (ec = rb->chains.first; ec; ec = next_rlc) { + next_rlc = ec->next; + if (MOD_lineart_chain_compute_length(ec) < threshold) { + BLI_remlink(&rb->chains, ec); } } } -int MOD_lineart_chain_count(const LineartLineChain *rlc) +int MOD_lineart_chain_count(const LineartEdgeChain *ec) { int count = 0; - LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { count++; } return count; @@ -894,8 +894,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb) if (rb == NULL) { return; } - LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) { - rlc->picked = 0; + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + ec->picked = 0; } } @@ -905,8 +905,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb) */ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad) { - LineartLineChain *rlc, *new_rlc; - LineartLineChainItem *rlci, *next_rlci, *prev_rlci; + LineartEdgeChain *ec, *new_rlc; + LineartEdgeChainItem *eci, *next_rlci, *prev_rlci; ListBase swap = {0}; swap.first = rb->chains.first; @@ -914,43 +914,43 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol rb->chains.last = rb->chains.first = NULL; - while ((rlc = BLI_pophead(&swap)) != NULL) { - rlc->next = rlc->prev = NULL; - BLI_addtail(&rb->chains, rlc); - LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first; - for (rlci = first_rlci->next; rlci; rlci = next_rlci) { - next_rlci = rlci->next; - prev_rlci = rlci->prev; + while ((ec = BLI_pophead(&swap)) != NULL) { + ec->next = ec->prev = NULL; + BLI_addtail(&rb->chains, ec); + LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first; + for (eci = first_rlci->next; eci; eci = next_rlci) { + next_rlci = eci->next; + prev_rlci = eci->prev; float angle = M_PI; if (next_rlci && prev_rlci) { - angle = angle_v2v2v2(prev_rlci->pos, rlci->pos, next_rlci->pos); + angle = angle_v2v2v2(prev_rlci->pos, eci->pos, next_rlci->pos); } else { break; /* No need to split at the last point anyway.*/ } if (angle < angle_threshold_rad) { new_rlc = lineart_chain_create(rb); - new_rlc->chain.first = rlci; - new_rlc->chain.last = rlc->chain.last; - rlc->chain.last = rlci->prev; - ((LineartLineChainItem *)rlc->chain.last)->next = 0; - rlci->prev = 0; + new_rlc->chain.first = eci; + new_rlc->chain.last = ec->chain.last; + ec->chain.last = eci->prev; + ((LineartEdgeChainItem *)ec->chain.last)->next = 0; + eci->prev = 0; /* End the previous one. */ lineart_chain_append_point(rb, - rlc, - rlci->pos, - rlci->gpos, - rlci->normal, - rlci->line_type, - rlc->level, - rlci->transparency_mask, - rlci->index); - new_rlc->object_ref = rlc->object_ref; - new_rlc->type = rlc->type; - new_rlc->level = rlc->level; - new_rlc->transparency_mask = rlc->transparency_mask; - rlc = new_rlc; + ec, + eci->pos, + eci->gpos, + eci->normal, + eci->line_type, + ec->level, + eci->transparency_mask, + eci->index); + new_rlc->object_ref = ec->object_ref; + new_rlc->type = ec->type; + new_rlc->level = ec->level; + new_rlc->transparency_mask = ec->transparency_mask; + ec = new_rlc; } } } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 52f7d3652a7..a9d17f19206 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -63,7 +63,7 @@ static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, LineartEdge *e); -static void lineart_bounding_area_link_line(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, LineartEdge *e); @@ -86,14 +86,14 @@ static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, - LineartTriangle *rt, + LineartTriangle *tri, double *LRUB, int recursive, int recursive_level, bool do_intersection); static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, - const LineartTriangle *rt, + const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, @@ -107,35 +107,35 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e); -static void lineart_discard_segment(LineartRenderBuffer *rb, LineartLineSegment *rls) +static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es) { BLI_spin_lock(&rb->lock_cuts); - memset(rls, 0, sizeof(LineartLineSegment)); + memset(es, 0, sizeof(LineartEdgeSegment)); /* Storing the node for potentially reuse the memory for new segment data. * Line Art data is not freed after all calculations are done. */ - BLI_addtail(&rb->wasted_cuts, rls); + BLI_addtail(&rb->wasted_cuts, es); BLI_spin_unlock(&rb->lock_cuts); } -static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb) +static LineartEdgeSegment *lineart_give_segment(LineartRenderBuffer *rb) { BLI_spin_lock(&rb->lock_cuts); /* See if there is any already allocated memory we can reuse. */ if (rb->wasted_cuts.first) { - LineartLineSegment *rls = (LineartLineSegment *)BLI_pophead(&rb->wasted_cuts); + LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&rb->wasted_cuts); BLI_spin_unlock(&rb->lock_cuts); - memset(rls, 0, sizeof(LineartLineSegment)); - return rls; + memset(es, 0, sizeof(LineartEdgeSegment)); + return es; } BLI_spin_unlock(&rb->lock_cuts); /* Otherwise allocate some new memory. */ - return (LineartLineSegment *)lineart_mem_acquire_thread(&rb->render_data_pool, - sizeof(LineartLineSegment)); + return (LineartEdgeSegment *)lineart_mem_acquire_thread(&rb->render_data_pool, + sizeof(LineartEdgeSegment)); } /** @@ -144,9 +144,9 @@ static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb) static void lineart_edge_cut( LineartRenderBuffer *rb, LineartEdge *e, double start, double end, uchar transparency_mask) { - LineartLineSegment *rls, *irls, *next_rls, *prev_rls; - LineartLineSegment *cut_start_before = 0, *cut_end_before = 0; - LineartLineSegment *ns = 0, *ns2 = 0; + LineartEdgeSegment *es, *ies, *next_es, *prev_es; + LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0; + LineartEdgeSegment *ns = 0, *ns2 = 0; int untouched = 0; /* If for some reason the occlusion function may give a result that has zero length, or reversed @@ -173,18 +173,18 @@ static void lineart_edge_cut( /* Begin looking for starting position of the segment. */ /* Not using a list iteration macro because of it more clear when using for loops to iterate * through the segments. */ - for (rls = e->segments.first; rls; rls = rls->next) { - if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, start)) { - cut_start_before = rls; + for (es = e->segments.first; es; es = es->next) { + if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, start)) { + cut_start_before = es; ns = cut_start_before; break; } - if (rls->next == NULL) { + if (es->next == NULL) { break; } - irls = rls->next; - if (irls->at > start + 1e-09 && start > rls->at) { - cut_start_before = irls; + ies = es->next; + if (ies->at > start + 1e-09 && start > es->at) { + cut_start_before = ies; ns = lineart_give_segment(rb); break; } @@ -192,25 +192,25 @@ static void lineart_edge_cut( if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { untouched = 1; } - for (rls = cut_start_before; rls; rls = rls->next) { + for (es = cut_start_before; es; es = es->next) { /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle * strip). */ - if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, end)) { - cut_end_before = rls; + if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, end)) { + cut_end_before = es; ns2 = cut_end_before; break; } - /* This check is to prevent `rls->at == 1.0` (where we don't need to cut because we are at the + /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the * end point). */ - if (!rls->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { - cut_end_before = rls; + if (!es->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { + cut_end_before = es; ns2 = cut_end_before; untouched = 1; break; } /* When an actual cut is needed in the line. */ - if (rls->at > end) { - cut_end_before = rls; + if (es->at > end) { + cut_end_before = es; ns2 = lineart_give_segment(rb); break; } @@ -233,9 +233,9 @@ static void lineart_edge_cut( if (cut_start_before) { if (cut_start_before != ns) { /* Insert cutting points for when a new cut is needed. */ - irls = cut_start_before->prev ? cut_start_before->prev : NULL; - ns->occlusion = irls ? irls->occlusion : 0; - ns->transparency_mask = irls->transparency_mask; + ies = cut_start_before->prev ? cut_start_before->prev : NULL; + ns->occlusion = ies ? ies->occlusion : 0; + ns->transparency_mask = ies->transparency_mask; BLI_insertlinkbefore(&e->segments, cut_start_before, ns); } /* Otherwise we already found a existing cutting point, no need to insert a new one. */ @@ -243,24 +243,24 @@ static void lineart_edge_cut( else { /* We have yet to reach a existing cutting point even after we searched the whole line, so we * append the new cut to the end. */ - irls = e->segments.last; - ns->occlusion = irls->occlusion; - ns->transparency_mask = irls->transparency_mask; + ies = e->segments.last; + ns->occlusion = ies->occlusion; + ns->transparency_mask = ies->transparency_mask; BLI_addtail(&e->segments, ns); } if (cut_end_before) { /* The same manipulation as on "cut_start_before". */ if (cut_end_before != ns2) { - irls = cut_end_before->prev ? cut_end_before->prev : NULL; - ns2->occlusion = irls ? irls->occlusion : 0; - ns2->transparency_mask = irls ? irls->transparency_mask : 0; + ies = cut_end_before->prev ? cut_end_before->prev : NULL; + ns2->occlusion = ies ? ies->occlusion : 0; + ns2->transparency_mask = ies ? ies->transparency_mask : 0; BLI_insertlinkbefore(&e->segments, cut_end_before, ns2); } } else { - irls = e->segments.last; - ns2->occlusion = irls->occlusion; - ns2->transparency_mask = irls->transparency_mask; + ies = e->segments.last; + ns2->occlusion = ies->occlusion; + ns2->transparency_mask = ies->transparency_mask; BLI_addtail(&e->segments, ns2); } @@ -276,29 +276,29 @@ static void lineart_edge_cut( } /* Register 1 level of occlusion for all touched segments. */ - for (rls = ns; rls && rls != ns2; rls = rls->next) { - rls->occlusion++; - rls->transparency_mask |= transparency_mask; + for (es = ns; es && es != ns2; es = es->next) { + es->occlusion++; + es->transparency_mask |= transparency_mask; } /* Reduce adjacent cutting points of the same level, which saves memory. */ char min_occ = 127; - prev_rls = NULL; - for (rls = e->segments.first; rls; rls = next_rls) { - next_rls = rls->next; + prev_es = NULL; + for (es = e->segments.first; es; es = next_es) { + next_es = es->next; - if (prev_rls && prev_rls->occlusion == rls->occlusion && - prev_rls->transparency_mask == rls->transparency_mask) { - BLI_remlink(&e->segments, rls); + if (prev_es && prev_es->occlusion == es->occlusion && + prev_es->transparency_mask == es->transparency_mask) { + BLI_remlink(&e->segments, es); /* This puts the node back to the render buffer, if more cut happens, these unused nodes get * picked first. */ - lineart_discard_segment(rb, rls); + lineart_discard_segment(rb, es); continue; } - min_occ = MIN2(min_occ, rls->occlusion); + min_occ = MIN2(min_occ, es->occlusion); - prev_rls = rls; + prev_es = es; } e->min_occ = min_occ; } @@ -306,12 +306,42 @@ static void lineart_edge_cut( /** * To see if given line is connected to an adjacent intersection line. */ -BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *rt) +BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri) { LineartVertIntersection *v1 = (void *)e->v1; LineartVertIntersection *v2 = (void *)e->v2; - return ((v1->base.flag && v1->intersecting_with == rt) || - (v2->base.flag && v2->intersecting_with == rt)); + return ((v1->base.flag && v1->intersecting_with == tri) || + (v2->base.flag && v2->intersecting_with == tri)); +} + +static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb, + LineartBoundingArea *ba, + LineartTriangle *tri) +{ + if (ba->triangle_count >= ba->max_triangle_count) { + LineartTriangle **new_array = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count * 2); + memcpy(new_array, ba->linked_triangles, sizeof(LineartTriangle *) * ba->max_triangle_count); + ba->max_triangle_count *= 2; + ba->linked_triangles = new_array; + } + ba->linked_triangles[ba->triangle_count] = tri; + ba->triangle_count++; +} + +static void lineart_bounding_area_line_add(LineartRenderBuffer *rb, + LineartBoundingArea *ba, + LineartEdge *e) +{ + if (ba->line_count >= ba->max_line_count) { + LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdge *) * ba->max_line_count * 2); + memcpy(new_array, ba->linked_lines, sizeof(LineartEdge *) * ba->max_line_count); + ba->max_line_count *= 2; + ba->linked_lines = new_array; + } + ba->linked_lines[ba->line_count] = e; + ba->line_count++; } static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id) @@ -319,7 +349,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1]; LineartBoundingArea *ba = lineart_edge_first_bounding_area(rb, e); LineartBoundingArea *nba = ba; - LineartTriangleThread *rt; + LineartTriangleThread *tri; /* These values are used for marching along the line. */ double l, r; @@ -334,16 +364,16 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * while (nba) { - LISTBASE_FOREACH (LinkData *, lip, &nba->linked_triangles) { - rt = lip->data; + for (int i = 0; i < nba->triangle_count; i++) { + tri = (LineartTriangleThread *)nba->linked_triangles[i]; /* If we are already testing the line in this thread, then don't do it. */ - if (rt->testing_e[thread_id] == e || (rt->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) || - lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)rt)) { + if (tri->testing_e[thread_id] == e || (tri->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) || + lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)tri)) { continue; } - rt->testing_e[thread_id] = e; + tri->testing_e[thread_id] = e; if (lineart_triangle_edge_image_space_occlusion(&rb->lock_task, - (const LineartTriangle *)rt, + (const LineartTriangle *)tri, e, rb->camera_pos, rb->cam_is_persp, @@ -354,7 +384,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * rb->shift_y, &l, &r)) { - lineart_edge_cut(rb, e, l, r, rt->base.transparency_mask); + lineart_edge_cut(rb, e, l, r, tri->base.transparency_mask); if (e->min_occ > rb->max_occlusion_level) { /* No need to calculate any longer on this line because no level more than set value is * going to show up in the rendered result. */ @@ -376,18 +406,18 @@ static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRend BLI_spin_lock(&rb->lock_task); #define LRT_ASSIGN_OCCLUSION_TASK(name) \ - if (rb->name##_managed) { \ - data = rb->name##_managed; \ - rti->name = (void *)data; \ + if (rb->name.last) { \ + data = rb->name.last; \ + rti->name.first = (void *)data; \ for (i = 0; i < LRT_THREAD_EDGE_COUNT && data; i++) { \ data = data->next; \ } \ - rti->name##_end = data; \ - rb->name##_managed = data; \ + rti->name.last = data; \ + rb->name.last = data; \ res = 1; \ } \ else { \ - rti->name = NULL; \ + rti->name.first = rti->name.last = NULL; \ } LRT_ASSIGN_OCCLUSION_TASK(contour); @@ -410,23 +440,23 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR while (lineart_occlusion_make_task_info(rb, rti)) { - for (eip = rti->contour; eip && eip != rti->contour_end; eip = eip->next) { + for (eip = rti->contour.first; eip && eip != rti->contour.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->crease; eip && eip != rti->crease_end; eip = eip->next) { + for (eip = rti->crease.first; eip && eip != rti->crease.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->intersection; eip && eip != rti->intersection_end; eip = eip->next) { + for (eip = rti->intersection.first; eip && eip != rti->intersection.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->material; eip && eip != rti->material_end; eip = eip->next) { + for (eip = rti->material.first; eip && eip != rti->material.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->edge_mark; eip && eip != rti->edge_mark_end; eip = eip->next) { + for (eip = rti->edge_mark.first; eip && eip != rti->edge_mark.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } } @@ -444,11 +474,13 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) "Task Pool"); int i; - rb->contour_managed = rb->contours; - rb->crease_managed = rb->crease_lines; - rb->intersection_managed = rb->intersection_lines; - rb->material_managed = rb->material_lines; - rb->edge_mark_managed = rb->edge_marks; + /* The "last" entry is used to store worker progress in the whole list. + * These list themselves are single-direction linked, with list.first being the head. */ + rb->contour.last = rb->contour.first; + rb->crease.last = rb->crease.first; + rb->intersection.last = rb->intersection.first; + rb->material.last = rb->material.first; + rb->edge_mark.last = rb->edge_mark.first; TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); @@ -627,75 +659,75 @@ static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1 */ static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb) { - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; /* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles * are relatively small. */ LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool, 64 * rb->triangle_size); - reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers, - &rb->render_data_pool, - render_triangles, - sizeof(LineartElementLinkNode)); - reln->element_count = 64; - reln->flags |= LRT_ELEMENT_IS_ADDITIONAL; + eln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers, + &rb->render_data_pool, + render_triangles, + sizeof(LineartElementLinkNode)); + eln->element_count = 64; + eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; - return reln; + return eln; } static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb) { - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVert) * 64); - reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers, - &rb->render_data_pool, - render_vertices, - sizeof(LineartElementLinkNode)); - reln->element_count = 64; - reln->flags |= LRT_ELEMENT_IS_ADDITIONAL; + eln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers, + &rb->render_data_pool, + render_vertices, + sizeof(LineartElementLinkNode)); + eln->element_count = 64; + eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; - return reln; + return eln; } static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer *rb) { - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64); - reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers, - &rb->render_data_pool, - render_edges, - sizeof(LineartElementLinkNode)); - reln->element_count = 64; - reln->crease_threshold = rb->crease_threshold; - reln->flags |= LRT_ELEMENT_IS_ADDITIONAL; + eln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers, + &rb->render_data_pool, + render_edges, + sizeof(LineartElementLinkNode)); + eln->element_count = 64; + eln->crease_threshold = rb->crease_threshold; + eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; - return reln; + return eln; } -static void lineart_triangle_post(LineartTriangle *rt, LineartTriangle *orig) +static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig) { /* Just re-assign normal and set cull flag. */ - copy_v3_v3_db(rt->gn, orig->gn); - rt->flags = LRT_CULL_GENERATED; + copy_v3_v3_db(tri->gn, orig->gn); + tri->flags = LRT_CULL_GENERATED; } -static void lineart_triangle_set_cull_flag(LineartTriangle *rt, uchar flag) +static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag) { - uchar intersection_only = (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY); - rt->flags = flag; - rt->flags |= intersection_only; + uchar intersection_only = (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY); + tri->flags = flag; + tri->flags |= intersection_only; } -static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int v2) +static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int v2) { - return ((rt->v[v1] == e->v1 && rt->v[v2] == e->v2) || - (rt->v[v2] == e->v1 && rt->v[v1] == e->v2)); + return ((tri->v[v1] == e->v1 && tri->v[v2] == e->v2) || + (tri->v[v2] == e->v1 && tri->v[v1] == e->v2)); } /** @@ -703,7 +735,7 @@ static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int * reversed by the caller so don't need to implement one in a different direction. */ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, int in0, int in1, int in2, @@ -728,26 +760,26 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, char new_flag = 0; LineartEdge *new_e, *e, *old_e; - LineartLineSegment *rls; - LineartTriangleAdjacent *rta; + LineartEdgeSegment *es; + LineartTriangleAdjacent *ta; - if (rt->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) { + if (tri->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) { return; } - /* See definition of rt->intersecting_verts and the usage in + /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for details. */ - rta = (void *)rt->intersecting_verts; + ta = (void *)tri->intersecting_verts; - LineartVert *rv = &((LineartVert *)v_eln->pointer)[v_count]; - LineartTriangle *rt1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count); - LineartTriangle *rt2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1)); + LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count]; + LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count); + LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1)); new_e = &((LineartEdge *)e_eln->pointer)[e_count]; - /* Init `rl` to the last `rl` entry. */ + /* Init `edge` to the last `edge` entry. */ e = new_e; -#define INCREASE_RL \ +#define INCREASE_EDGE \ e_count++; \ v1_obi = e->v1_obindex; \ v2_obi = e->v2_obindex; \ @@ -755,40 +787,40 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, e = new_e; \ e->v1_obindex = v1_obi; \ e->v2_obindex = v2_obi; \ - rls = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineSegment)); \ - BLI_addtail(&e->segments, rls); + es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \ + BLI_addtail(&e->segments, es); -#define SELECT_RL(e_num, v1_link, v2_link, newrt) \ - if (rta->e[e_num]) { \ - old_e = rta->e[e_num]; \ +#define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \ + if (ta->e[e_num]) { \ + old_e = ta->e[e_num]; \ new_flag = old_e->flags; \ old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - INCREASE_RL \ + INCREASE_EDGE \ e->v1 = (v1_link); \ e->v2 = (v2_link); \ e->flags = new_flag; \ e->object_ref = ob; \ - e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \ - e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \ + e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ + e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ lineart_add_edge_to_list(rb, e); \ } -#define RELINK_RL(e_num, newrt) \ - if (rta->e[e_num]) { \ - old_e = rta->e[e_num]; \ - old_e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \ - old_e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \ +#define RELINK_EDGE(e_num, new_tri) \ + if (ta->e[e_num]) { \ + old_e = ta->e[e_num]; \ + old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ + old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ } -#define REMOVE_TRIANGLE_RL \ - if (rta->e[0]) { \ - rta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ +#define REMOVE_TRIANGLE_EDGE \ + if (ta->e[0]) { \ + ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ } \ - if (rta->e[1]) { \ - rta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + if (ta->e[1]) { \ + ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ } \ - if (rta->e[2]) { \ - rta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + if (ta->e[2]) { \ + ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ } switch (in0 + in1 + in2) { @@ -797,13 +829,13 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, case 3: /* Triangle completely behind near plane, throw it away * also remove render lines form being computed. */ - lineart_triangle_set_cull_flag(rt, LRT_CULL_DISCARD); - REMOVE_TRIANGLE_RL + lineart_triangle_set_cull_flag(tri, LRT_CULL_DISCARD); + REMOVE_TRIANGLE_EDGE return; case 2: /* Two points behind near plane, cut those and * generate 2 new points, 3 lines and 1 triangle. */ - lineart_triangle_set_cull_flag(rt, LRT_CULL_USED); + lineart_triangle_set_cull_flag(tri, LRT_CULL_USED); /** * (!in0) means "when point 0 is visible". @@ -828,136 +860,136 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, if (!in0) { /* Cut point for line 2---|-----0. */ - sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc); + sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); /* Assign it to a new point. */ - interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[2]->index; /* Cut point for line 1---|-----0. */ - sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc); + sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); /* Assign it to another new point. */ - interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[1]->index; /* New line connecting two new points. */ - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as - * `rt->rl` and `rt->v` has the same sequence. and the winding direction + * `tri->edge` and `tri->v` has the same sequence. and the winding direction * can be either CW or CCW but needs to be consistent throughout the calculation. */ - e->v1 = &rv[1]; - e->v2 = &rv[0]; + e->v1 = &vt[1]; + e->v2 = &vt[0]; /* Only one adjacent triangle, because the other side is the near plane. */ /* Use `tl` or `tr` doesn't matter. */ - e->t1 = rt1; + e->t1 = tri1; e->object_ref = ob; /* New line connecting original point 0 and a new point, only when it's a selected line. */ - SELECT_RL(2, rt->v[0], &rv[0], rt1) + SELECT_EDGE(2, tri->v[0], &vt[0], tri1) /* New line connecting original point 0 and another new point. */ - SELECT_RL(0, rt->v[0], &rv[1], rt1) + SELECT_EDGE(0, tri->v[0], &vt[1], tri1) /* Re-assign triangle point array to two new points. */ - rt1->v[0] = rt->v[0]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[0]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; - lineart_triangle_post(rt1, rt); + lineart_triangle_post(tri1, tri); v_count += 2; t_count += 1; } else if (!in2) { - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[0]->index; - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[1]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } - e->v1 = &rv[0]; - e->v2 = &rv[1]; - e->t1 = rt1; + e->v1 = &vt[0]; + e->v2 = &vt[1]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(2, rt->v[2], &rv[0], rt1) - SELECT_RL(1, rt->v[2], &rv[1], rt1) + SELECT_EDGE(2, tri->v[2], &vt[0], tri1) + SELECT_EDGE(1, tri->v[2], &vt[1], tri1) - rt1->v[0] = &rv[0]; - rt1->v[1] = &rv[1]; - rt1->v[2] = rt->v[2]; + tri1->v[0] = &vt[0]; + tri1->v[1] = &vt[1]; + tri1->v[2] = tri->v[2]; - lineart_triangle_post(rt1, rt); + lineart_triangle_post(tri1, tri); v_count += 2; t_count += 1; } else if (!in1) { - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[0]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(1, rt->v[1], &rv[0], rt1) - SELECT_RL(0, rt->v[1], &rv[1], rt1) + SELECT_EDGE(1, tri->v[1], &vt[0], tri1) + SELECT_EDGE(0, tri->v[1], &vt[1], tri1) - rt1->v[0] = &rv[0]; - rt1->v[1] = rt->v[1]; - rt1->v[2] = &rv[1]; + tri1->v[0] = &vt[0]; + tri1->v[1] = tri->v[1]; + tri1->v[2] = &vt[1]; - lineart_triangle_post(rt1, rt); + lineart_triangle_post(tri1, tri); v_count += 2; t_count += 1; @@ -966,7 +998,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, case 1: /* One point behind near plane, cut those and * generate 2 new points, 4 lines and 2 triangles. */ - lineart_triangle_set_cull_flag(rt, LRT_CULL_USED); + lineart_triangle_set_cull_flag(tri, LRT_CULL_USED); /** * (in0) means "when point 0 is invisible". @@ -993,152 +1025,152 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, */ if (in0) { /* Cut point for line 0---|------1. */ - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot2 / (dot1 + dot2); /* Assign to a new point. */ - interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[0]->index; /* Cut point for line 0---|------2. */ - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot2 / (dot1 + dot2); /* Assign to other new point. */ - interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[0]->index; /* New line connects two new points. */ - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; /* New line connects new point 0 and old point 1, * this is a border line. */ - SELECT_RL(0, rt->v[1], &rv[0], rt1) - SELECT_RL(2, rt->v[2], &rv[1], rt2) - RELINK_RL(1, rt2) + SELECT_EDGE(0, tri->v[1], &vt[0], tri1) + SELECT_EDGE(2, tri->v[2], &vt[1], tri2) + RELINK_EDGE(1, tri2) /* We now have one triangle closed. */ - rt1->v[0] = rt->v[1]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[1]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; /* Close the second triangle. */ - rt2->v[0] = &rv[1]; - rt2->v[1] = rt->v[1]; - rt2->v[2] = rt->v[2]; + tri2->v[0] = &vt[1]; + tri2->v[1] = tri->v[1]; + tri2->v[2] = tri->v[2]; - lineart_triangle_post(rt1, rt); - lineart_triangle_post(rt2, rt); + lineart_triangle_post(tri1, tri); + lineart_triangle_post(tri2, tri); v_count += 2; t_count += 2; } else if (in1) { - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[1]->index; - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[1]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(1, rt->v[2], &rv[0], rt1) - SELECT_RL(0, rt->v[0], &rv[1], rt2) - RELINK_RL(2, rt2) + SELECT_EDGE(1, tri->v[2], &vt[0], tri1) + SELECT_EDGE(0, tri->v[0], &vt[1], tri2) + RELINK_EDGE(2, tri2) - rt1->v[0] = rt->v[2]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[2]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; - rt2->v[0] = &rv[1]; - rt2->v[1] = rt->v[2]; - rt2->v[2] = rt->v[0]; + tri2->v[0] = &vt[1]; + tri2->v[1] = tri->v[2]; + tri2->v[2] = tri->v[0]; - lineart_triangle_post(rt1, rt); - lineart_triangle_post(rt2, rt); + lineart_triangle_post(tri1, tri); + lineart_triangle_post(tri2, tri); v_count += 2; t_count += 2; } else if (in2) { - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[2]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(2, rt->v[0], &rv[0], rt1) - SELECT_RL(1, rt->v[1], &rv[1], rt2) - RELINK_RL(0, rt2) + SELECT_EDGE(2, tri->v[0], &vt[0], tri1) + SELECT_EDGE(1, tri->v[1], &vt[1], tri2) + RELINK_EDGE(0, tri2) - rt1->v[0] = rt->v[0]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[0]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; - rt2->v[0] = &rv[1]; - rt2->v[1] = rt->v[0]; - rt2->v[2] = rt->v[1]; + tri2->v[0] = &vt[1]; + tri2->v[1] = tri->v[0]; + tri2->v[2] = tri->v[1]; - lineart_triangle_post(rt1, rt); - lineart_triangle_post(rt2, rt); + lineart_triangle_post(tri1, tri); + lineart_triangle_post(tri2, tri); v_count += 2; t_count += 2; @@ -1149,10 +1181,10 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, *r_e_count = e_count; *r_t_count = t_count; -#undef INCREASE_RL -#undef SELECT_RL -#undef RELINK_RL -#undef REMOVE_TRIANGLE_RL +#undef INCREASE_EDGE +#undef SELECT_EDGE +#undef RELINK_EDGE +#undef REMOVE_TRIANGLE_EDGE } /** @@ -1163,7 +1195,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, */ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) { - LineartTriangle *rt; + LineartTriangle *tri; LineartElementLinkNode *v_eln, *t_eln, *e_eln; double(*vp)[4] = rb->view_projection; int i; @@ -1219,25 +1251,25 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) in0 = 0, in1 = 0, in2 = 0; \ if (clip_far) { \ /* Point outside far plane. */ \ - if (rt->v[0]->fbcoord[use_w] > clip_end) { \ + if (tri->v[0]->fbcoord[use_w] > clip_end) { \ in0 = 1; \ } \ - if (rt->v[1]->fbcoord[use_w] > clip_end) { \ + if (tri->v[1]->fbcoord[use_w] > clip_end) { \ in1 = 1; \ } \ - if (rt->v[2]->fbcoord[use_w] > clip_end) { \ + if (tri->v[2]->fbcoord[use_w] > clip_end) { \ in2 = 1; \ } \ } \ else { \ /* Point inside near plane. */ \ - if (rt->v[0]->fbcoord[use_w] < clip_start) { \ + if (tri->v[0]->fbcoord[use_w] < clip_start) { \ in0 = 1; \ } \ - if (rt->v[1]->fbcoord[use_w] < clip_start) { \ + if (tri->v[1]->fbcoord[use_w] < clip_start) { \ in1 = 1; \ } \ - if (rt->v[2]->fbcoord[use_w] < clip_start) { \ + if (tri->v[2]->fbcoord[use_w] < clip_start) { \ in2 = 1; \ } \ } @@ -1252,19 +1284,19 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) } /* Then go through all the other triangles. */ - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - if (reln->flags & LRT_ELEMENT_IS_ADDITIONAL) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) { continue; } - ob = reln->object_ref; - for (i = 0; i < reln->element_count; i++) { + ob = eln->object_ref; + for (i = 0; i < eln->element_count; i++) { /* Select the triangle in the array. */ - rt = (void *)(((uchar *)reln->pointer) + rb->triangle_size * i); + tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i); LRT_CULL_DECIDE_INSIDE LRT_CULL_ENSURE_MEMORY lineart_triangle_cull_single(rb, - rt, + tri, in0, in1, in2, @@ -1298,40 +1330,40 @@ static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb) while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) { MEM_freeN(ld->data); } - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - LineartTriangle *rt = reln->pointer; + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + LineartTriangle *tri = eln->pointer; int i; - for (i = 0; i < reln->element_count; i++) { - /* See definition of rt->intersecting_verts and the usage in + for (i = 0; i < eln->element_count; i++) { + /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for detailed. */ - rt->intersecting_verts = NULL; - rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size); + tri->intersecting_verts = NULL; + tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); } } } static void lineart_main_perspective_division(LineartRenderBuffer *rb) { - LineartVert *rv; + LineartVert *vt; int i; if (!rb->cam_is_persp) { return; } - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->vertex_buffer_pointers) { - rv = reln->pointer; - for (i = 0; i < reln->element_count; i++) { + 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. */ - rv[i].fbcoord[0] /= rv[i].fbcoord[3]; - rv[i].fbcoord[1] /= rv[i].fbcoord[3]; + 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. */ - // `rv[i].fbcoord[2] = -2 * rv[i].fbcoord[2] / (far - near) - (far + near) / (far - near); - rv[i].fbcoord[0] -= rb->shift_x * 2; - rv[i].fbcoord[1] -= rb->shift_y * 2; + // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); + vt[i].fbcoord[0] -= rb->shift_x * 2; + vt[i].fbcoord[1] -= rb->shift_y * 2; } } } @@ -1343,10 +1375,10 @@ static void lineart_vert_transform( BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4]) { double co[4]; - LineartVert *rv = &RvBuf[index]; + LineartVert *vt = &RvBuf[index]; copy_v3db_v3fl(co, v->co); - mul_v3_m4v3_db(rv->gloc, mv_mat, co); - mul_v4_m4v3_db(rv->fbcoord, mvp_mat, co); + mul_v3_m4v3_db(vt->gloc, mv_mat, co); + mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co); } /** @@ -1381,12 +1413,12 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb, return LRT_EDGE_FLAG_CONTOUR; } - LineartTriangle *rt1, *rt2; + LineartTriangle *tri1, *tri2; LineartVert *l; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - rt1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); - rt2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); + tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); + tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); l = &rv_array[BM_elem_index_get(e->v1)]; @@ -1403,14 +1435,14 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb, view_vector = rb->view_vector; } - dot_1 = dot_v3v3_db(view_vector, rt1->gn); - dot_2 = dot_v3v3_db(view_vector, rt2->gn); + 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 && (dot_1 + dot_2)) { return LRT_EDGE_FLAG_CONTOUR; } - if (rb->use_crease && (dot_v3v3_db(rt1->gn, rt2->gn) < crease_threshold)) { + if (rb->use_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { if (!no_crease) { return LRT_EDGE_FLAG_CREASE; } @@ -1431,35 +1463,35 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) { switch (e->flags) { case LRT_EDGE_FLAG_CONTOUR: - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); break; case LRT_EDGE_FLAG_CREASE: - lineart_prepend_edge_direct(&rb->crease_lines, e); + lineart_prepend_edge_direct(&rb->crease.first, e); break; case LRT_EDGE_FLAG_MATERIAL: - lineart_prepend_edge_direct(&rb->material_lines, e); + lineart_prepend_edge_direct(&rb->material.first, e); break; case LRT_EDGE_FLAG_EDGE_MARK: - lineart_prepend_edge_direct(&rb->edge_marks, e); + lineart_prepend_edge_direct(&rb->edge_mark.first, e); break; case LRT_EDGE_FLAG_INTERSECTION: - lineart_prepend_edge_direct(&rb->intersection_lines, e); + lineart_prepend_edge_direct(&rb->intersection.first, e); break; } } -static void lineart_triangle_adjacent_assign(LineartTriangle *rt, - LineartTriangleAdjacent *rta, +static void lineart_triangle_adjacent_assign(LineartTriangle *tri, + LineartTriangleAdjacent *ta, LineartEdge *e) { - if (lineart_edge_match(rt, e, 0, 1)) { - rta->e[0] = e; + if (lineart_edge_match(tri, e, 0, 1)) { + ta->e[0] = e; } - else if (lineart_edge_match(rt, e, 1, 2)) { - rta->e[1] = e; + else if (lineart_edge_match(tri, e, 1, 2)) { + ta->e[1] = e; } - else if (lineart_edge_match(rt, e, 2, 0)) { - rta->e[2] = e; + else if (lineart_edge_match(tri, e, 2, 0)) { + ta->e[2] = e; } } @@ -1477,11 +1509,11 @@ static void lineart_geometry_object_load(Depsgraph *dg, BMEdge *e; BMLoop *loop; LineartEdge *la_e; - LineartTriangle *rt; + LineartTriangle *tri; LineartTriangleAdjacent *orta; double new_mvp[4][4], new_mv[4][4], normal[4][4]; float imat[4][4]; - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; LineartVert *orv; LineartEdge *o_la_e; LineartTriangle *ort; @@ -1581,10 +1613,10 @@ static void lineart_geometry_object_load(Depsgraph *dg, orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob; - reln = lineart_list_append_pointer_pool_sized( + eln = lineart_list_append_pointer_pool_sized( &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - reln->element_count = bm->totvert; - reln->object_ref = orig_ob; + eln->element_count = bm->totvert; + eln->object_ref = orig_ob; if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { use_crease = cosf(M_PI - ob->lineart.crease_threshold); @@ -1596,14 +1628,14 @@ static void lineart_geometry_object_load(Depsgraph *dg, /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates * erroneous detection on creases. Future configuration should allow options. */ if (ob->type == OB_FONT) { - reln->flags |= LRT_ELEMENT_BORDER_ONLY; + eln->flags |= LRT_ELEMENT_BORDER_ONLY; } - reln = lineart_list_append_pointer_pool_sized( + eln = lineart_list_append_pointer_pool_sized( &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - reln->element_count = bm->totface; - reln->object_ref = orig_ob; - reln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); + eln->element_count = bm->totface; + eln->object_ref = orig_ob; + eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); /* Note this memory is not from pool, will be deleted after culling. */ orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); @@ -1621,39 +1653,39 @@ static void lineart_geometry_object_load(Depsgraph *dg, * index to come close together. */ (*global_vindex) += bm->totvert; - rt = ort; + tri = ort; for (i = 0; i < bm->totface; i++) { f = BM_face_at_index(bm, i); loop = f->l_first; - rt->v[0] = &orv[BM_elem_index_get(loop->v)]; + tri->v[0] = &orv[BM_elem_index_get(loop->v)]; loop = loop->next; - rt->v[1] = &orv[BM_elem_index_get(loop->v)]; + tri->v[1] = &orv[BM_elem_index_get(loop->v)]; loop = loop->next; - rt->v[2] = &orv[BM_elem_index_get(loop->v)]; + tri->v[2] = &orv[BM_elem_index_get(loop->v)]; /* Transparency bit assignment. */ Material *mat = BKE_object_material_get(ob, f->mat_nr + 1); - rt->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? - mat->lineart.transparency_mask : - 0); + tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? + mat->lineart.transparency_mask : + 0); double gn[3]; copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(rt->gn, normal, gn); - normalize_v3_db(rt->gn); + mul_v3_mat3_m4v3_db(tri->gn, normal, gn); + normalize_v3_db(tri->gn); if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - rt->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; } else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - rt->flags |= LRT_TRIANGLE_NO_INTERSECTION; + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; } /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - rt->intersecting_verts = (void *)&orta[i]; + tri->intersecting_verts = (void *)&orta[i]; - rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size); + tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); } /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ @@ -1676,10 +1708,10 @@ static void lineart_geometry_object_load(Depsgraph *dg, } o_la_e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - reln = lineart_list_append_pointer_pool_sized( + eln = lineart_list_append_pointer_pool_sized( &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - reln->element_count = allocate_la_e; - reln->object_ref = orig_ob; + eln->element_count = allocate_la_e; + eln->object_ref = orig_ob; la_e = o_la_e; for (i = 0; i < bm->totedge; i++) { @@ -1707,9 +1739,9 @@ static void lineart_geometry_object_load(Depsgraph *dg, la_e->flags = e->head.hflag; la_e->object_ref = orig_ob; - LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartLineSegment)); - BLI_addtail(&la_e->segments, rls); + LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdgeSegment)); + BLI_addtail(&la_e->segments, es); if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { lineart_add_edge_to_list(rb, la_e); } @@ -1875,51 +1907,51 @@ static void lineart_main_load_geometries( * Returns the two other verts of the triangle given a vertex. Returns false if the given vertex * doesn't belong to this triangle. */ -static bool lineart_triangle_get_other_verts(const LineartTriangle *rt, - const LineartVert *rv, +static bool lineart_triangle_get_other_verts(const LineartTriangle *tri, + const LineartVert *vt, LineartVert **l, LineartVert **r) { - if (rt->v[0] == rv) { - *l = rt->v[1]; - *r = rt->v[2]; + if (tri->v[0] == vt) { + *l = tri->v[1]; + *r = tri->v[2]; return true; } - if (rt->v[1] == rv) { - *l = rt->v[2]; - *r = rt->v[0]; + if (tri->v[1] == vt) { + *l = tri->v[2]; + *r = tri->v[0]; return true; } - if (rt->v[2] == rv) { - *l = rt->v[0]; - *r = rt->v[1]; + if (tri->v[2] == vt) { + *l = tri->v[0]; + *r = tri->v[1]; return true; } return false; } -static bool lineart_edge_from_triangle(const LineartTriangle *rt, +static bool lineart_edge_from_triangle(const LineartTriangle *tri, const LineartEdge *e, bool allow_overlapping_edges) { /* Normally we just determine from the pointer address. */ - if (e->t1 == rt || e->t2 == rt) { + if (e->t1 == tri || e->t2 == tri) { return true; } /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one * edge is from specific triangle. This is slower but can handle edge split cases very well. */ if (allow_overlapping_edges) { -#define LRT_TRI_SAME_POINT(rt, i, pt) \ - ((LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])) || \ - (LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2]))) - if ((LRT_TRI_SAME_POINT(rt, 0, e->v1) || LRT_TRI_SAME_POINT(rt, 1, e->v1) || - LRT_TRI_SAME_POINT(rt, 2, e->v1)) && - (LRT_TRI_SAME_POINT(rt, 0, e->v2) || LRT_TRI_SAME_POINT(rt, 1, e->v2) || - LRT_TRI_SAME_POINT(rt, 2, e->v2))) { +#define LRT_TRI_SAME_POINT(tri, i, pt) \ + ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \ + (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2]))) + if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) || + LRT_TRI_SAME_POINT(tri, 2, e->v1)) && + (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) || + LRT_TRI_SAME_POINT(tri, 2, e->v2))) { return true; } #undef LRT_TRI_SAME_POINT @@ -1961,7 +1993,7 @@ static bool lineart_edge_from_triangle(const LineartTriangle *rt, * in ratio from `e->v1` to `e->v2`. The line is later cut with these two values. */ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), - const LineartTriangle *rt, + const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, @@ -1988,8 +2020,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), double gloc[4], trans[4]; double cut = -1; - double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = rt->v[0]->fbcoord, - *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord; + double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord, + *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord; /* Overlapping not possible, return early. */ if ((MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN2(LFBC[0], RFBC[0])) || @@ -2001,7 +2033,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } /* If the the line is one of the edge in the triangle, then it's not occluded. */ - if (lineart_edge_from_triangle(rt, e, allow_overlapping_edges)) { + if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) { return false; } @@ -2013,8 +2045,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), /* Sort the intersection distance. */ INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order); - sub_v3_v3v3_db(Lv, e->v1->gloc, rt->v[0]->gloc); - sub_v3_v3v3_db(Rv, e->v2->gloc, rt->v[0]->gloc); + sub_v3_v3v3_db(Lv, e->v1->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(Rv, e->v2->gloc, tri->v[0]->gloc); copy_v3_v3_db(Cv, camera_dir); @@ -2025,14 +2057,18 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), copy_v4_v4_db(vd4, override_camera_loc); } if (override_cam_is_persp) { - sub_v3_v3v3_db(Cv, vd4, rt->v[0]->gloc); + sub_v3_v3v3_db(Cv, vd4, tri->v[0]->gloc); } - dot_l = dot_v3v3_db(Lv, rt->gn); - dot_r = dot_v3v3_db(Rv, rt->gn); - dot_f = dot_v3v3_db(Cv, rt->gn); + dot_l = dot_v3v3_db(Lv, tri->gn); + dot_r = dot_v3v3_db(Rv, tri->gn); + dot_f = dot_v3v3_db(Cv, tri->gn); - if (!dot_f) { + /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_ + * faces in perspective mode would get erroneously caught in this condition where they really are + * legit faces that would produce occlusion, but haven't encountered those yet in my test files. + */ + if (fabs(dot_f) < FLT_EPSILON) { return false; } @@ -2271,17 +2307,17 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l, /** * To save time and prevent overlapping lines when computing intersection lines. */ -static bool lineart_vert_already_intersected_2v(LineartVertIntersection *rv, +static bool lineart_vert_already_intersected_2v(LineartVertIntersection *vt, LineartVertIntersection *v1, LineartVertIntersection *v2) { - return ((rv->isec1 == v1->base.index && rv->isec2 == v2->base.index) || - (rv->isec2 == v2->base.index && rv->isec1 == v1->base.index)); + return ((vt->isec1 == v1->base.index && vt->isec2 == v2->base.index) || + (vt->isec2 == v2->base.index && vt->isec1 == v1->base.index)); } -static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, LineartVert *v2) +static void lineart_vert_set_intersection_2v(LineartVert *vt, LineartVert *v1, LineartVert *v2) { - LineartVertIntersection *irv = (LineartVertIntersection *)rv; + LineartVertIntersection *irv = (LineartVertIntersection *)vt; irv->isec1 = v1->index; irv->isec2 = v2->index; } @@ -2294,7 +2330,7 @@ static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, L static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *rb, LineartVert *v1, LineartVert *v2, - LineartTriangle *rt, + LineartTriangle *tri, LineartTriangle *testing, LineartVert *last) { @@ -2306,11 +2342,11 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r LineartVert *l = v1, *r = v2; for (LinkNode *ln = (void *)testing->intersecting_verts; ln; ln = ln->next) { - LineartVertIntersection *rv = ln->link; - if (rv->intersecting_with == rt && + LineartVertIntersection *vt = ln->link; + if (vt->intersecting_with == tri && lineart_vert_already_intersected_2v( - rv, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) { - return (LineartVert *)rv; + vt, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) { + return (LineartVert *)vt; } } @@ -2360,7 +2396,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r * Test if two triangles intersect. Generates one intersection line if the check succeeds. */ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, LineartTriangle *testing) { LineartVert *v1 = 0, *v2 = 0; @@ -2379,14 +2415,14 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, ZMax = rb->far_clip; ZMin = rb->near_clip; copy_v3_v3_db(cl, rb->camera_pos); - LineartVert *share = lineart_triangle_share_point(testing, rt); + LineartVert *share = lineart_triangle_share_point(testing, tri); if (share) { /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc` * against `acd` or `cd` against `abc`. */ LineartVert *new_share; - lineart_triangle_get_other_verts(rt, share, &sv1, &sv2); + lineart_triangle_get_other_verts(tri, share, &sv1, &sv2); v1 = new_share = lineart_mem_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection))); @@ -2394,47 +2430,47 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, copy_v3_v3_db(new_share->gloc, share->gloc); - v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, rt, testing, 0); + v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, tri, testing, 0); if (v2 == NULL) { lineart_triangle_get_other_verts(testing, share, &sv1, &sv2); - v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, rt, 0); + v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, tri, 0); if (v2 == NULL) { return 0; } lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, new_share); } else { - lineart_prepend_pool(&rt->intersecting_verts, &rb->render_data_pool, new_share); + lineart_prepend_pool(&tri->intersecting_verts, &rb->render_data_pool, new_share); } } else { /* If not sharing any points, then we need to try all the possibilities. */ - E0T = lineart_triangle_2v_intersection_test(rb, rt->v[0], rt->v[1], rt, testing, 0); + E0T = lineart_triangle_2v_intersection_test(rb, tri->v[0], tri->v[1], tri, testing, 0); if (E0T && (!(*next))) { (*next) = E0T; - lineart_vert_set_intersection_2v((*next), rt->v[0], rt->v[1]); + lineart_vert_set_intersection_2v((*next), tri->v[0], tri->v[1]); next = &v2; } - E1T = lineart_triangle_2v_intersection_test(rb, rt->v[1], rt->v[2], rt, testing, v1); + E1T = lineart_triangle_2v_intersection_test(rb, tri->v[1], tri->v[2], tri, testing, v1); if (E1T && (!(*next))) { (*next) = E1T; - lineart_vert_set_intersection_2v((*next), rt->v[1], rt->v[2]); + lineart_vert_set_intersection_2v((*next), tri->v[1], tri->v[2]); next = &v2; } if (!(*next)) { - E2T = lineart_triangle_2v_intersection_test(rb, rt->v[2], rt->v[0], rt, testing, v1); + E2T = lineart_triangle_2v_intersection_test(rb, tri->v[2], tri->v[0], tri, testing, v1); } if (E2T && (!(*next))) { (*next) = E2T; - lineart_vert_set_intersection_2v((*next), rt->v[2], rt->v[0]); + lineart_vert_set_intersection_2v((*next), tri->v[2], tri->v[0]); next = &v2; } if (!(*next)) { TE0 = lineart_triangle_2v_intersection_test( - rb, testing->v[0], testing->v[1], testing, rt, v1); + rb, testing->v[0], testing->v[1], testing, tri, v1); } if (TE0 && (!(*next))) { (*next) = TE0; @@ -2443,7 +2479,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } if (!(*next)) { TE1 = lineart_triangle_2v_intersection_test( - rb, testing->v[1], testing->v[2], testing, rt, v1); + rb, testing->v[1], testing->v[2], testing, tri, v1); } if (TE1 && (!(*next))) { (*next) = TE1; @@ -2452,7 +2488,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } if (!(*next)) { TE2 = lineart_triangle_2v_intersection_test( - rb, testing->v[2], testing->v[0], testing, rt, v1); + rb, testing->v[2], testing->v[0], testing, tri, v1); } if (TE2 && (!(*next))) { (*next) = TE2; @@ -2484,70 +2520,67 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin)); v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin)); - ((LineartVertIntersection *)v1)->intersecting_with = rt; + ((LineartVertIntersection *)v1)->intersecting_with = tri; ((LineartVertIntersection *)v2)->intersecting_with = testing; result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge)); result->v1 = v1; result->v2 = v2; - result->t1 = rt; + result->t1 = tri; result->t2 = testing; - LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineSegment)); - BLI_addtail(&result->segments, rls); + LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); + BLI_addtail(&result->segments, es); /* Don't need to OR flags right now, just a type mark. */ result->flags = LRT_EDGE_FLAG_INTERSECTION; - lineart_prepend_edge_direct(&rb->intersection_lines, result); + + lineart_prepend_edge_direct(&rb->intersection.first, result); int r1, r2, c1, c2, row, col; if (lineart_get_edge_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { for (col = c1; col != c2 + 1; col++) { - lineart_bounding_area_link_line( + lineart_bounding_area_link_edge( rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], result); } } } - rb->intersection_count++; - return result; } static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, LineartBoundingArea *ba) { /* Testing_triangle->testing[0] is used to store pairing triangle reference. * See definition of LineartTriangleThread for more info. */ LineartTriangle *testing_triangle; - LineartTriangleThread *rtt; - LinkData *lip, *next_lip; + LineartTriangleThread *tt; - double *G0 = rt->v[0]->gloc, *G1 = rt->v[1]->gloc, *G2 = rt->v[2]->gloc; + double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; /* If this is not the smallest subdiv bounding area.*/ if (ba->child) { - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[0]); - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[1]); - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[2]); - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[3]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[2]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[3]); return; } /* If this _is_ the smallest subdiv bounding area, then do the intersections there. */ - for (lip = ba->linked_triangles.first; lip; lip = next_lip) { - next_lip = lip->next; - testing_triangle = lip->data; - rtt = (LineartTriangleThread *)testing_triangle; + for (int i = 0; i < ba->triangle_count; i++) { + testing_triangle = ba->linked_triangles[i]; + tt = (LineartTriangleThread *)testing_triangle; - if (testing_triangle == rt || rtt->testing_e[0] == (LineartEdge *)rt) { + if (testing_triangle == tri || tt->testing_e[0] == (LineartEdge *)tri) { continue; } - rtt->testing_e[0] = (LineartEdge *)rt; + tt->testing_e[0] = (LineartEdge *)tri; if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) || ((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) && - (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) { + (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) { continue; } @@ -2561,12 +2594,12 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, (MAX3(G0[0], G1[0], G2[0]) < MIN3(RG0[0], RG1[0], RG2[0])) || (MIN3(G0[1], G1[1], G2[1]) > MAX3(RG0[1], RG1[1], RG2[1])) || (MAX3(G0[1], G1[1], G2[1]) < MIN3(RG0[1], RG1[1], RG2[1])) || - lineart_triangle_share_edge(rt, testing_triangle)) { + lineart_triangle_share_edge(tri, testing_triangle)) { continue; } /* If we do need to compute intersection, then finally do it. */ - lineart_triangle_intersect(rb, rt, testing_triangle); + lineart_triangle_intersect(rb, tri, testing_triangle); } } @@ -2598,22 +2631,11 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) return; } - rb->contour_count = 0; - rb->contour_managed = NULL; - rb->intersection_count = 0; - rb->intersection_managed = NULL; - rb->material_line_count = 0; - rb->material_managed = NULL; - rb->crease_count = 0; - rb->crease_managed = NULL; - rb->edge_mark_count = 0; - rb->edge_mark_managed = NULL; - - rb->contours = NULL; - rb->intersection_lines = NULL; - rb->crease_lines = NULL; - rb->material_lines = NULL; - rb->edge_marks = NULL; + memset(&rb->contour, 0, sizeof(ListBase)); + memset(&rb->crease, 0, sizeof(ListBase)); + memset(&rb->intersection, 0, sizeof(ListBase)); + memset(&rb->edge_mark, 0, sizeof(ListBase)); + memset(&rb->material, 0, sizeof(ListBase)); BLI_listbase_clear(&rb->chains); BLI_listbase_clear(&rb->wasted_cuts); @@ -2671,6 +2693,13 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->w = scene->r.xsch; rb->h = scene->r.ysch; + if (rb->cam_is_persp) { + rb->tile_recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE; + } + else { + rb->tile_recursive_level = LRT_TILE_RECURSIVE_ORTHO; + } + double asp = ((double)rb->w / (double)rb->h); rb->shift_x = (asp >= 1) ? c->shiftx : c->shiftx * asp; rb->shift_y = (asp <= 1) ? c->shifty : c->shifty * asp; @@ -2746,6 +2775,14 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) ba->cx = (ba->l + ba->r) / 2; ba->cy = (ba->u + ba->b) / 2; + /* Init linked_triangles array. */ + ba->max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT; + ba->max_line_count = LRT_TILE_EDGE_COUNT_INITIAL; + ba->linked_triangles = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count); + ba->linked_lines = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdge *) * ba->max_line_count); + /* Link adjacent ones. */ if (row) { lineart_list_append_pointer_pool( @@ -2925,7 +2962,7 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, { LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartBoundingArea) * 4); - LineartTriangle *rt; + LineartTriangle *tri; LineartEdge *e; ba[0].l = root->cx; @@ -2960,35 +2997,47 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, lineart_bounding_areas_connect_new(rb, root); - while ((rt = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) { + /* Init linked_triangles array. */ + for (int i = 0; i < 4; i++) { + ba[i].max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT; + ba[i].max_line_count = LRT_TILE_EDGE_COUNT_INITIAL; + ba[i].linked_triangles = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangle *) * LRT_TILE_SPLITTING_TRIANGLE_LIMIT); + ba[i].linked_lines = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdge *) * LRT_TILE_EDGE_COUNT_INITIAL); + } + + for (int i = 0; i < root->triangle_count; i++) { + tri = root->linked_triangles[i]; LineartBoundingArea *cba = root->child; double b[4]; - b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); - b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); + b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); + b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); if (LRT_BOUND_AREA_CROSSES(b, &cba[0].l)) { - lineart_bounding_area_link_triangle(rb, &cba[0], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[0], tri, b, 0, recursive_level + 1, false); } if (LRT_BOUND_AREA_CROSSES(b, &cba[1].l)) { - lineart_bounding_area_link_triangle(rb, &cba[1], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[1], tri, b, 0, recursive_level + 1, false); } if (LRT_BOUND_AREA_CROSSES(b, &cba[2].l)) { - lineart_bounding_area_link_triangle(rb, &cba[2], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[2], tri, b, 0, recursive_level + 1, false); } if (LRT_BOUND_AREA_CROSSES(b, &cba[3].l)) { - lineart_bounding_area_link_triangle(rb, &cba[3], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[3], tri, b, 0, recursive_level + 1, false); } } - while ((e = lineart_list_pop_pointer_no_free(&root->linked_lines)) != NULL) { - lineart_bounding_area_link_line(rb, root, e); + for (int i = 0; i < root->line_count; i++) { + e = root->linked_lines[i]; + lineart_bounding_area_link_edge(rb, root, e); } rb->bounding_area_count += 3; } -static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb), +static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb), const double l[2], const double r[2], LineartBoundingArea *ba) @@ -3032,11 +3081,11 @@ static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb) } static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, - LineartTriangle *rt, + LineartTriangle *tri, LineartBoundingArea *ba) { double p1[2], p2[2], p3[2], p4[2]; - double *FBC1 = rt->v[0]->fbcoord, *FBC2 = rt->v[1]->fbcoord, *FBC3 = rt->v[2]->fbcoord; + double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord; p3[0] = p1[0] = (double)ba->l; p2[1] = p1[1] = (double)ba->b; @@ -3056,9 +3105,9 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, return true; } - if ((lineart_bounding_area_line_intersect(fb, FBC1, FBC2, ba)) || - (lineart_bounding_area_line_intersect(fb, FBC2, FBC3, ba)) || - (lineart_bounding_area_line_intersect(fb, FBC3, FBC1, ba))) { + if ((lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba)) || + (lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba)) || + (lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba))) { return true; } @@ -3071,27 +3120,27 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, */ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, - LineartTriangle *rt, + LineartTriangle *tri, double *LRUB, int recursive, int recursive_level, bool do_intersection) { - if (!lineart_bounding_area_triangle_intersect(rb, rt, root_ba)) { + if (!lineart_bounding_area_triangle_intersect(rb, tri, root_ba)) { return; } if (root_ba->child == NULL) { - lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, rt); - root_ba->triangle_count++; + lineart_bounding_area_triangle_add(rb, root_ba, tri); /* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore. * Here we use recursive limit. This is especially useful in orthographic render, * where a lot of faces could easily line up perfectly in image space, * which can not be separated by simply slicing the image tile. */ - if (root_ba->triangle_count > 200 && recursive && recursive_level < 10) { + if (root_ba->triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT && recursive && + recursive_level < rb->tile_recursive_level) { lineart_bounding_area_split(rb, root_ba, recursive_level); } if (recursive && do_intersection && rb->use_intersections) { - lineart_triangle_intersect_in_bounding_area(rb, rt, root_ba); + lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba); } } else { @@ -3099,54 +3148,54 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, double *B1 = LRUB; double b[4]; if (!LRUB) { - b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); - b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); + b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); + b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); B1 = b; } if (LRT_BOUND_AREA_CROSSES(B1, &ba[0].l)) { lineart_bounding_area_link_triangle( - rb, &ba[0], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[0], tri, B1, recursive, recursive_level + 1, do_intersection); } if (LRT_BOUND_AREA_CROSSES(B1, &ba[1].l)) { lineart_bounding_area_link_triangle( - rb, &ba[1], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[1], tri, B1, recursive, recursive_level + 1, do_intersection); } if (LRT_BOUND_AREA_CROSSES(B1, &ba[2].l)) { lineart_bounding_area_link_triangle( - rb, &ba[2], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[2], tri, B1, recursive, recursive_level + 1, do_intersection); } if (LRT_BOUND_AREA_CROSSES(B1, &ba[3].l)) { lineart_bounding_area_link_triangle( - rb, &ba[3], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[3], tri, B1, recursive, recursive_level + 1, do_intersection); } } } -static void lineart_bounding_area_link_line(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, LineartEdge *e) { if (root_ba->child == NULL) { - lineart_list_append_pointer_pool(&root_ba->linked_lines, &rb->render_data_pool, e); + lineart_bounding_area_line_add(rb, root_ba, e); } else { - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) { - lineart_bounding_area_link_line(rb, &root_ba->child[0], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[0], e); } - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) { - lineart_bounding_area_link_line(rb, &root_ba->child[1], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[1], e); } - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) { - lineart_bounding_area_link_line(rb, &root_ba->child[2], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[2], e); } - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) { - lineart_bounding_area_link_line(rb, &root_ba->child[3], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[3], e); } } } @@ -3162,7 +3211,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb) if (lineart_get_edge_bounding_areas(rb, e, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { for (col = c1; col != c2 + 1; col++) { - lineart_bounding_area_link_line( + lineart_bounding_area_link_edge( rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], e); } } @@ -3172,7 +3221,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb) } static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, @@ -3181,14 +3230,14 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; double b[4]; - if (!rt->v[0] || !rt->v[1] || !rt->v[2]) { + if (!tri->v[0] || !tri->v[1] || !tri->v[2]) { return false; } - b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[2] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); - b[3] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); + b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[2] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); + b[3] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) { return false; @@ -3355,33 +3404,33 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub */ static void lineart_main_add_triangles(LineartRenderBuffer *rb) { - LineartTriangle *rt; + LineartTriangle *tri; int i, lim; int x1, x2, y1, y2; int r, co; - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - rt = reln->pointer; - lim = reln->element_count; + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + tri = eln->pointer; + lim = eln->element_count; for (i = 0; i < lim; i++) { - if ((rt->flags & LRT_CULL_USED) || (rt->flags & LRT_CULL_DISCARD)) { - rt = (void *)(((uchar *)rt) + rb->triangle_size); + if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) { + tri = (void *)(((uchar *)tri) + rb->triangle_size); continue; } - if (lineart_get_triangle_bounding_areas(rb, rt, &y1, &y2, &x1, &x2)) { + if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) { for (co = x1; co <= x2; co++) { for (r = y1; r <= y2; r++) { lineart_bounding_area_link_triangle(rb, &rb->initial_bounding_areas[r * LRT_BA_ROWS + co], - rt, + tri, 0, 1, 0, - (!(rt->flags & LRT_TRIANGLE_NO_INTERSECTION))); + (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION))); } } } /* Else throw away. */ - rt = (void *)(((uchar *)rt) + rb->triangle_size); + tri = (void *)(((uchar *)tri) + rb->triangle_size); } } } @@ -3656,6 +3705,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ + BKE_scene_camera_switch_update(scene); + if (!scene->camera) { return false; } @@ -3815,52 +3866,52 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; - LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { - if (rlc->picked) { + if (ec->picked) { continue; } - if (!(rlc->type & (types & enabled_types))) { + if (!(ec->type & (types & enabled_types))) { continue; } - if (rlc->level > level_end || rlc->level < level_start) { + if (ec->level > level_end || ec->level < level_start) { continue; } - if (orig_ob && orig_ob != rlc->object_ref) { + if (orig_ob && orig_ob != ec->object_ref) { continue; } - if (orig_col && rlc->object_ref) { - if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)rlc->object_ref)) { + if (orig_col && ec->object_ref) { + if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) { continue; } } if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_ENABLE) { if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_MATCH) { - if (rlc->transparency_mask != transparency_mask) { + if (ec->transparency_mask != transparency_mask) { continue; } } else { - if (!(rlc->transparency_mask & transparency_mask)) { + if (!(ec->transparency_mask & transparency_mask)) { continue; } } } /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */ - // rlc->picked = 1; + // ec->picked = 1; int array_idx = 0; - int count = MOD_lineart_chain_count(rlc); + int count = MOD_lineart_chain_count(ec); bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false); float *stroke_data = MEM_callocN(sizeof(float) * count * GP_PRIM_DATABUF_SIZE, "line art add stroke"); - LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) { - stroke_data[array_idx] = rlci->gpos[0]; - stroke_data[array_idx + 1] = rlci->gpos[1]; - stroke_data[array_idx + 2] = rlci->gpos[2]; + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + stroke_data[array_idx] = eci->gpos[0]; + stroke_data[array_idx + 1] = eci->gpos[1]; + stroke_data[array_idx + 2] = eci->gpos[2]; mul_m4_v3(gp_obmat_inverse, &stroke_data[array_idx]); stroke_data[array_idx + 3] = 1; /* thickness. */ stroke_data[array_idx + 4] = opacity; /* hardness?. */ @@ -3874,7 +3925,7 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, MEM_freeN(stroke_data); if (source_vgname && vgname) { - Object *eval_ob = DEG_get_evaluated_object(depsgraph, rlc->object_ref); + Object *eval_ob = DEG_get_evaluated_object(depsgraph, ec->object_ref); int gpdg = -1; if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) { if (eval_ob && eval_ob->type == OB_MESH) { @@ -3890,8 +3941,8 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, } } int sindex = 0, vindex; - LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) { - vindex = rlci->index; + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + vindex = eci->index; if (vindex >= me->totvert) { break; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 47ca6e45bd5..9ed98b38f07 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -58,7 +58,7 @@ void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size); void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size); void lineart_mem_destroy(struct LineartStaticMemPool *smp); -void lineart_prepend_edge_direct(struct LineartEdge **first, void *node); +void lineart_prepend_edge_direct(void **list_head, void *node); void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link); void lineart_matrix_ortho_44d(double (*mProjection)[4], @@ -76,29 +76,42 @@ int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb); void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb); #define LRT_ITER_ALL_LINES_BEGIN \ - LineartEdge *e, *next_e, **current_list; \ - e = rb->contours; \ - for (current_list = &rb->contours; e; e = next_e) { \ + LineartEdge *e, *next_e; \ + void **current_head; \ + e = rb->contour.first; \ + if (!e) { \ + e = rb->crease.first; \ + } \ + if (!e) { \ + e = rb->material.first; \ + } \ + if (!e) { \ + e = rb->edge_mark.first; \ + } \ + if (!e) { \ + e = rb->intersection.first; \ + } \ + for (current_head = &rb->contour.first; e; e = next_e) { \ next_e = e->next; #define LRT_ITER_ALL_LINES_NEXT \ while (!next_e) { \ - if (current_list == &rb->contours) { \ - current_list = &rb->crease_lines; \ + if (current_head == &rb->contour.first) { \ + current_head = &rb->crease.first; \ } \ - else if (current_list == &rb->crease_lines) { \ - current_list = &rb->material_lines; \ + else if (current_head == &rb->crease.first) { \ + current_head = &rb->material.first; \ } \ - else if (current_list == &rb->material_lines) { \ - current_list = &rb->edge_marks; \ + else if (current_head == &rb->material.first) { \ + current_head = &rb->edge_mark.first; \ } \ - else if (current_list == &rb->edge_marks) { \ - current_list = &rb->intersection_lines; \ + else if (current_head == &rb->edge_mark.first) { \ + current_head = &rb->intersection.first; \ } \ else { \ break; \ } \ - next_e = *current_list; \ + next_e = *current_head; \ } #define LRT_ITER_ALL_LINES_END \ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index dcb1f9cde5d..d05f931f75d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -135,11 +135,11 @@ void lineart_mem_destroy(LineartStaticMemPool *smp) } } -void lineart_prepend_edge_direct(LineartEdge **first, void *node) +void lineart_prepend_edge_direct(void **list_head, void *node) { LineartEdge *e_n = (LineartEdge *)node; - e_n->next = (*first); - (*first) = e_n; + e_n->next = (*list_head); + (*list_head) = e_n; } void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index f1ffd7827b8..cf6009c2881 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC intern/gpu_buffers.c intern/gpu_capabilities.cc intern/gpu_codegen.c + intern/gpu_compute.cc intern/gpu_context.cc intern/gpu_debug.cc intern/gpu_drawlist.cc @@ -91,6 +92,7 @@ set(SRC opengl/gl_backend.cc opengl/gl_batch.cc + opengl/gl_compute.cc opengl/gl_context.cc opengl/gl_debug.cc opengl/gl_debug_layer.cc @@ -113,6 +115,7 @@ set(SRC GPU_buffers.h GPU_capabilities.h GPU_common.h + GPU_compute.h GPU_context.h GPU_debug.h GPU_drawlist.h @@ -163,6 +166,7 @@ set(SRC opengl/gl_backend.hh opengl/gl_batch.hh + opengl/gl_compute.hh opengl/gl_context.hh opengl/gl_debug.hh opengl/gl_drawlist.hh @@ -390,6 +394,7 @@ if(WITH_GTESTS) if(WITH_OPENGL_DRAW_TESTS) set(TEST_SRC tests/gpu_testing.cc + tests/gpu_shader_test.cc tests/gpu_testing.hh ) diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index f54ecece659..0c054d4f264 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -37,6 +37,8 @@ int GPU_max_textures(void); int GPU_max_textures_vert(void); int GPU_max_textures_geom(void); int GPU_max_textures_frag(void); +int GPU_max_work_group_count(int index); +int GPU_max_work_group_size(int index); int GPU_max_uniforms_vert(void); int GPU_max_uniforms_frag(void); int GPU_max_batch_indices(void); @@ -55,6 +57,8 @@ bool GPU_use_main_context_workaround(void); bool GPU_use_hq_normals_workaround(void); bool GPU_crappy_amd_driver(void); +bool GPU_compute_shader_support(void); +bool GPU_shader_storage_buffer_objects_support(void); bool GPU_shader_image_load_store_support(void); bool GPU_mem_stats_supported(void); diff --git a/source/blender/gpu/GPU_compute.h b/source/blender/gpu/GPU_compute.h new file mode 100644 index 00000000000..a048f72c0a0 --- /dev/null +++ b/source/blender/gpu/GPU_compute.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_sys_types.h" + +#include "GPU_shader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_compute_dispatch(GPUShader *shader, + uint groups_x_len, + uint groups_y_len, + uint groups_z_len); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 76aab3c196b..8362dbcaccc 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -49,6 +49,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uin /* supports only GPU_PRIM_POINTS, GPU_PRIM_LINES and GPU_PRIM_TRIS. */ void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len); +GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len); void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v); void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *); @@ -70,6 +71,8 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem); GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *); void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *); +void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding); + /* Create a sub-range of an existing index-buffer. */ GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length); void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, @@ -77,6 +80,15 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, uint start, uint length); +/** + * (Download and) return a pointer containing the data of an index buffer. + * + * Note that the returned pointer is still owned by the driver. To get an + * local copy, use `GPU_indexbuf_unmap` after calling `GPU_indexbuf_read`. + */ +const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem); +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer); + void GPU_indexbuf_discard(GPUIndexBuf *elem); bool GPU_indexbuf_is_init(GPUIndexBuf *elem); diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index aad6ae9e2ba..e073263f352 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec const float proj[4][4], const int view[4]); -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_win[3]); - -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]); - -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[3]); + +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[2]); + +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]); + +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, + const float win[3], + float r_world[3]); /* 2D Projection Matrix */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 9824c7016dc..3923c920c9e 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,6 +27,7 @@ extern "C" { #endif +struct GPUIndexBuf; struct GPUVertBuf; /** Opaque type hiding #blender::gpu::Shader */ @@ -45,6 +46,10 @@ GPUShader *GPU_shader_create(const char *vertcode, const char *libcode, const char *defines, const char *shname); +GPUShader *GPU_shader_create_compute(const char *computecode, + const char *libcode, + const char *defines, + const char *shname); GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, @@ -53,6 +58,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, + const char *computecode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -126,6 +132,7 @@ int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); +int GPU_shader_get_ssbo(GPUShader *shader, const char *name); int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name); int GPU_shader_get_texture_binding(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 0687f271670..a338728804c 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -39,6 +39,7 @@ typedef enum eGPUBarrier { GPU_BARRIER_NONE = 0, GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0), GPU_BARRIER_TEXTURE_FETCH = (1 << 1), + GPU_BARRIER_SHADER_STORAGE = (1 << 2), } eGPUBarrier; ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH) diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index aae58de533b..2c54016daa7 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -59,6 +59,7 @@ typedef enum { GPU_USAGE_STREAM, GPU_USAGE_STATIC, /* do not keep data in memory */ GPU_USAGE_DYNAMIC, + GPU_USAGE_DEVICE_ONLY, /* Do not do host->device data transfers. */ } GPUUsageType; /** Opaque type hiding blender::gpu::VertBuf. */ @@ -70,6 +71,14 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp #define GPU_vertbuf_create_with_format(format) \ GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_STATIC) +/** + * (Download and) return a pointer containing the data of a vertex buffer. + * + * Note that the returned pointer is still owned by the driver. To get an + * local copy, use `GPU_vertbuf_unmap` after calling `GPU_vertbuf_read`. + */ +const void *GPU_vertbuf_read(GPUVertBuf *verts); +void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data); void GPU_vertbuf_clear(GPUVertBuf *verts); void GPU_vertbuf_discard(GPUVertBuf *); @@ -138,6 +147,7 @@ uint GPU_vertbuf_get_vertex_len(const GPUVertBuf *verts); GPUVertBufStatus GPU_vertbuf_get_status(const GPUVertBuf *verts); void GPU_vertbuf_use(GPUVertBuf *); +void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding); /* XXX do not use. */ void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data); diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 04ec82a9213..73792215569 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -47,6 +47,7 @@ class GPUBackend { static GPUBackend *get(void); virtual void samplers_update(void) = 0; + virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0; virtual Context *context_alloc(void *ghost_window) = 0; diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index d8764502800..c6e9dc210cb 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -82,6 +82,16 @@ int GPU_max_textures(void) return GCaps.max_textures; } +int GPU_max_work_group_count(int index) +{ + return GCaps.max_work_group_count[index]; +} + +int GPU_max_work_group_size(int index) +{ + return GCaps.max_work_group_size[index]; +} + int GPU_max_uniforms_vert(void) { return GCaps.max_uniforms_vert; @@ -148,6 +158,16 @@ bool GPU_use_hq_normals_workaround(void) return GCaps.use_hq_normals_workaround; } +bool GPU_compute_shader_support(void) +{ + return GCaps.compute_shader_support; +} + +bool GPU_shader_storage_buffer_objects_support(void) +{ + return GCaps.shader_storage_buffer_objects_support; +} + bool GPU_shader_image_load_store_support(void) { return GCaps.shader_image_load_store_support; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 7c1d4590ce8..95cf7fd335d 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -41,6 +41,8 @@ struct GPUCapabilities { int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; + int max_work_group_count[3] = {0, 0, 0}; + int max_work_group_size[3] = {0, 0, 0}; int max_uniforms_vert = 0; int max_uniforms_frag = 0; int max_batch_indices = 0; @@ -51,6 +53,8 @@ struct GPUCapabilities { const char *(*extension_get)(int); bool mem_stats_support = false; + bool compute_shader_support = false; + bool shader_storage_buffer_objects_support = false; bool shader_image_load_store_support = false; /* OpenGL related workarounds. */ bool mip_render_workaround = false; diff --git a/source/blender/gpu/intern/gpu_compute.cc b/source/blender/gpu/intern/gpu_compute.cc new file mode 100644 index 00000000000..7a8ae2acf9a --- /dev/null +++ b/source/blender/gpu/intern/gpu_compute.cc @@ -0,0 +1,41 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#include "GPU_compute.h" + +#include "gpu_backend.hh" + +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_compute_dispatch(GPUShader *shader, + uint groups_x_len, + uint groups_y_len, + uint groups_z_len) +{ + blender::gpu::GPUBackend &gpu_backend = *blender::gpu::GPUBackend::get(); + GPU_shader_bind(shader); + gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len); +} + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 65932d2dbf4..20a26c0fe9d 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -31,6 +31,8 @@ #include "gpu_index_buffer_private.hh" +#include <cstring> + #define KEEP_SINGLE_COPY 1 #define RESTART_INDEX 0xFFFFFFFF @@ -66,6 +68,14 @@ void GPU_indexbuf_init(GPUIndexBufBuilder *builder, GPU_indexbuf_init_ex(builder, prim_type, prim_len * (uint)verts_per_prim, vertex_len); } +GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len) +{ + GPUIndexBuf *elem_ = GPU_indexbuf_calloc(); + IndexBuf *elem = unwrap(elem_); + elem->init_build_on_device(index_len); + return elem_; +} + void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v) { #if TRUST_NO_ONE @@ -241,6 +251,15 @@ void IndexBuf::init(uint indices_len, uint32_t *indices) #endif } +void IndexBuf::init_build_on_device(uint index_len) +{ + is_init_ = true; + index_start_ = 0; + index_len_ = index_len; + index_type_ = GPU_INDEX_U32; + data_ = nullptr; +} + void IndexBuf::init_subrange(IndexBuf *elem_src, uint start, uint length) { /* We don't support nested subranges. */ @@ -307,6 +326,14 @@ void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx) } } +uint32_t *IndexBuf::unmap(const uint32_t *mapped_memory) const +{ + size_t size = size_get(); + uint32_t *result = static_cast<uint32_t *>(MEM_mallocN(size, __func__)); + memcpy(result, mapped_memory, size); + return result; +} + } // namespace blender::gpu /** \} */ @@ -351,6 +378,16 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, unwrap(elem)->init_subrange(unwrap(elem_src), start, length); } +const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem) +{ + return unwrap(elem)->read(); +} + +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer) +{ + return unwrap(elem)->unmap(mapped_buffer); +} + void GPU_indexbuf_discard(GPUIndexBuf *elem) { delete unwrap(elem); @@ -366,4 +403,9 @@ int GPU_indexbuf_primitive_len(GPUPrimType prim_type) return indices_per_primitive(prim_type); } +void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding) +{ + unwrap(elem)->bind_as_ssbo(binding); +} + /** \} */ diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh index 2405db8664a..358258604bf 100644 --- a/source/blender/gpu/intern/gpu_index_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh @@ -75,13 +75,14 @@ class IndexBuf { void init(uint indices_len, uint32_t *indices); void init_subrange(IndexBuf *elem_src, uint start, uint length); + void init_build_on_device(uint index_len); uint32_t index_len_get(void) const { return index_len_; } /* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */ - size_t size_get(void) + size_t size_get(void) const { return index_len_ * to_bytesize(index_type_); }; @@ -91,6 +92,11 @@ class IndexBuf { return is_init_; }; + virtual void bind_as_ssbo(uint binding) = 0; + + virtual const uint32_t *read() const = 0; + uint32_t *unmap(const uint32_t *mapped_memory) const; + private: inline void squeeze_indices_short(uint min_idx, uint max_idx); inline uint index_range(uint *r_min, uint *r_max); @@ -105,6 +111,10 @@ static inline IndexBuf *unwrap(GPUIndexBuf *indexbuf) { return reinterpret_cast<IndexBuf *>(indexbuf); } +static inline const IndexBuf *unwrap(const GPUIndexBuf *indexbuf) +{ + return reinterpret_cast<const IndexBuf *>(indexbuf); +} static inline int indices_per_primitive(GPUPrimType prim_type) { diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 569b51a407a..6eb9cb823d5 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX, GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ); } -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float win[3]) +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[3]) { float v[4]; @@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3], win[2] = (v[2] + 1) * 0.5f; } +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[2]) +{ + float v[4]; + + mul_v4_m4v3(v, model, world); + mul_m4_v4(proj, v); + + if (v[3] != 0.0f) { + mul_v2_fl(v, 1.0f / v[3]); + } + + win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f; + win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; +} + /** * The same result could be obtained as follows: * @@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, return true; } -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, + const float win[3], + float r_world[3]) { float in[3] = { (win[0] - precalc->view[0]) / precalc->view[2], @@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc * mul_v3_m4v3(r_world, precalc->model_inverted, in); } -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]) +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]) { struct GPUMatrixUnproject_Precalc precalc; if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { zero_v3(r_world); return false; } - GPU_matrix_unproject_with_precalc(&precalc, win, r_world); + GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); return true; } diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index aea27756708..265dec7c56a 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -290,6 +290,7 @@ static void standard_defines(Vector<const char *> &sources) GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, + const char *computecode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -297,8 +298,10 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, const int tf_count, const char *shname) { - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != nullptr) && (vertcode != nullptr)); + /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ + BLI_assert(((fragcode != nullptr) && (vertcode != nullptr) && (computecode == nullptr)) || + ((fragcode == nullptr) && (vertcode == nullptr) && (geomcode == nullptr) && + (computecode != nullptr))); Shader *shader = GPUBackend::get()->shader_alloc(shname); @@ -349,6 +352,21 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, shader->geometry_shader_from_glsl(sources); } + if (computecode) { + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_COMPUTE_SHADER\n"); + if (defines) { + sources.append(defines); + } + if (libcode) { + sources.append(libcode); + } + sources.append(computecode); + + shader->compute_shader_from_glsl(sources); + } + if (tf_names != nullptr && tf_count > 0) { BLI_assert(tf_type != GPU_SHADER_TFB_NONE); shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type); @@ -380,8 +398,33 @@ GPUShader *GPU_shader_create(const char *vertcode, const char *defines, const char *shname) { - return GPU_shader_create_ex( - vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, nullptr, 0, shname); + return GPU_shader_create_ex(vertcode, + fragcode, + geomcode, + nullptr, + libcode, + defines, + GPU_SHADER_TFB_NONE, + nullptr, + 0, + shname); +} + +GPUShader *GPU_shader_create_compute(const char *computecode, + const char *libcode, + const char *defines, + const char *shname) +{ + return GPU_shader_create_ex(nullptr, + nullptr, + nullptr, + computecode, + libcode, + defines, + GPU_SHADER_TFB_NONE, + nullptr, + 0, + shname); } GPUShader *GPU_shader_create_from_python(const char *vertcode, @@ -402,6 +445,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPUShader *sh = GPU_shader_create_ex(vertcode, fragcode, geomcode, + nullptr, libcode, defines, GPU_SHADER_TFB_NONE, @@ -567,6 +611,13 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +int GPU_shader_get_ssbo(GPUShader *shader, const char *name) +{ + ShaderInterface *interface = unwrap(shader)->interface; + const ShaderInput *ssbo = interface->ssbo_get(name); + return ssbo ? ssbo->location : -1; +} + /* DEPRECATED. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) { diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index c584c40eca8..ae94112b17b 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -80,6 +80,8 @@ void ShaderInterface::debug_print() Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_); Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_); Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_); + Span<ShaderInput> ssbos = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_ + uniform_len_, + ssbo_len_); char *name_buf = name_buffer_; const char format[] = " | %.8x : %4d : %s\n"; @@ -117,6 +119,13 @@ void ShaderInterface::debug_print() } } + if (ssbos.size() > 0) { + printf("\n Shader Storage Objects :\n"); + } + for (const ShaderInput &ssbo : ssbos) { + printf(format, ssbo.name_hash, ssbo.binding, name_buf + ssbo.name_offset); + } + printf("\n"); } diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index aec58544111..ebed7b15170 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -60,6 +60,7 @@ class ShaderInterface { uint attr_len_ = 0; uint ubo_len_ = 0; uint uniform_len_ = 0; + uint ssbo_len_ = 0; /** Enabled bind-points that needs to be fed with data. */ uint16_t enabled_attr_mask_ = 0; uint16_t enabled_ubo_mask_ = 0; @@ -99,6 +100,11 @@ class ShaderInterface { return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding); } + inline const ShaderInput *ssbo_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name); + } + inline const char *input_name_get(const ShaderInput *input) const { return name_buffer_ + input->name_offset; diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh index d9327bbc0f4..281f01dbc22 100644 --- a/source/blender/gpu/intern/gpu_shader_private.hh +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -49,6 +49,7 @@ class Shader { virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0; virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0; virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual void compute_shader_from_glsl(MutableSpan<const char *> sources) = 0; virtual bool finalize(void) = 0; virtual void transform_feedback_names_set(Span<const char *> name_list, diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 09b9eba9f95..3ecbb740a0c 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -149,6 +149,16 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts_) return wrap(unwrap(verts_)->duplicate()); } +const void *GPU_vertbuf_read(GPUVertBuf *verts) +{ + return unwrap(verts)->read(); +} + +void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data) +{ + return unwrap(verts)->unmap(mapped_data); +} + /** Same as discard but does not free. */ void GPU_vertbuf_clear(GPUVertBuf *verts) { @@ -324,6 +334,11 @@ void GPU_vertbuf_use(GPUVertBuf *verts) unwrap(verts)->upload(); } +void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding) +{ + unwrap(verts)->bind_as_ssbo(binding); +} + /* XXX this is just a wrapper for the use of the Hair refine workaround. * To be used with GPU_vertbuf_use(). */ void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data) diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index 67a09f6f83c..9531c2c1a5f 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -66,6 +66,7 @@ class VertBuf { void allocate(uint vert_len); void resize(uint vert_len); void upload(void); + virtual void bind_as_ssbo(uint binding) = 0; VertBuf *duplicate(void); @@ -96,6 +97,8 @@ class VertBuf { } virtual void update_sub(uint start, uint len, void *data) = 0; + virtual const void *read() const = 0; + virtual void *unmap(const void *mapped_data) const = 0; protected: virtual void acquire_data(void) = 0; diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 31b6549fc3b..d85f9f7684d 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -437,6 +437,16 @@ void GLBackend::capabilities_init() GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo; GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store; + GCaps.compute_shader_support = GLEW_ARB_compute_shader; + if (GCaps.compute_shader_support) { + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); + } + GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size); diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 231e5811b45..e9dcdffced0 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -28,6 +28,7 @@ #include "BLI_vector.hh" #include "gl_batch.hh" +#include "gl_compute.hh" #include "gl_context.hh" #include "gl_drawlist.hh" #include "gl_framebuffer.hh" @@ -126,6 +127,12 @@ class GLBackend : public GPUBackend { return shared_orphan_list_; }; + void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override + { + GLContext::get()->state_manager_active_get()->apply_state(); + GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len); + } + private: static void platform_init(void); static void platform_exit(void); diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc new file mode 100644 index 00000000000..fa8317dde4a --- /dev/null +++ b/source/blender/gpu/opengl/gl_compute.cc @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#include "gl_compute.hh" + +#include "gl_debug.hh" + +#include "glew-mx.h" + +namespace blender::gpu { + +void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len) +{ + glDispatchCompute(group_x_len, group_y_len, group_z_len); + debug::check_gl_error("Dispatch Compute"); +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_compute.hh b/source/blender/gpu/opengl/gl_compute.hh new file mode 100644 index 00000000000..2fd918ddd10 --- /dev/null +++ b/source/blender/gpu/opengl/gl_compute.hh @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +namespace blender::gpu { + +class GLCompute { + public: + static void dispatch(int group_x_len, int group_y_len, int group_z_len); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_index_buffer.cc b/source/blender/gpu/opengl/gl_index_buffer.cc index e2c18c5d0b9..e305f765ad9 100644 --- a/source/blender/gpu/opengl/gl_index_buffer.cc +++ b/source/blender/gpu/opengl/gl_index_buffer.cc @@ -40,17 +40,14 @@ void GLIndexBuf::bind() return; } - if (ibo_id_ == 0) { + const bool allocate_on_device = ibo_id_ == 0; + if (allocate_on_device) { glGenBuffers(1, &ibo_id_); - - if (data_ == nullptr) { - debug::raise_gl_error("Trying to use Index Buffer but the buffer contains no data"); - } } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_); - if (data_ != nullptr) { + if (data_ != nullptr || allocate_on_device) { size_t size = this->size_get(); /* Sends data to GPU. */ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data_, GL_STATIC_DRAW); @@ -59,4 +56,29 @@ void GLIndexBuf::bind() } } +void GLIndexBuf::bind_as_ssbo(uint binding) +{ + bind(); + BLI_assert(ibo_id_ != 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_); +} + +const uint32_t *GLIndexBuf::read() const +{ + BLI_assert(is_active()); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + uint32_t *result = static_cast<uint32_t *>(data); + return result; +} + +bool GLIndexBuf::is_active() const +{ + if (!ibo_id_) { + return false; + } + int active_ibo_id = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &active_ibo_id); + return ibo_id_ == active_ibo_id; +} + } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_index_buffer.hh b/source/blender/gpu/opengl/gl_index_buffer.hh index b84934bb77f..0dbdaa6d398 100644 --- a/source/blender/gpu/opengl/gl_index_buffer.hh +++ b/source/blender/gpu/opengl/gl_index_buffer.hh @@ -34,6 +34,7 @@ namespace blender::gpu { class GLIndexBuf : public IndexBuf { friend class GLBatch; friend class GLDrawList; + friend class GLShader; /* For compute shaders. */ private: GLuint ibo_id_ = 0; @@ -42,6 +43,9 @@ class GLIndexBuf : public IndexBuf { ~GLIndexBuf(); void bind(void); + void bind_as_ssbo(uint binding) override; + + const uint32_t *read() const override; void *offset_ptr(uint additional_vertex_offset) const { @@ -57,6 +61,9 @@ class GLIndexBuf : public IndexBuf { return (index_type_ == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu; } + private: + bool is_active() const; + MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf") }; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index dd08a67517e..e77347d99eb 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -26,6 +26,7 @@ #include "BLI_string.h" #include "BLI_vector.hh" +#include "GPU_capabilities.h" #include "GPU_platform.h" #include "gl_backend.hh" @@ -63,6 +64,7 @@ GLShader::~GLShader() glDeleteShader(vert_shader_); glDeleteShader(geom_shader_); glDeleteShader(frag_shader_); + glDeleteShader(compute_shader_); glDeleteProgram(shader_program_); } @@ -72,7 +74,7 @@ GLShader::~GLShader() /** \name Shader stage creation * \{ */ -char *GLShader::glsl_patch_get() +static char *glsl_patch_default_get() { /** Used for shader patching. Init once. */ static char patch[512] = "\0"; @@ -111,6 +113,30 @@ char *GLShader::glsl_patch_get() return patch; } +static char *glsl_patch_compute_get() +{ + /** Used for shader patching. Init once. */ + static char patch[512] = "\0"; + if (patch[0] != '\0') { + return patch; + } + + size_t slen = 0; + /* Version need to go first. */ + STR_CONCAT(patch, slen, "#version 430\n"); + STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n"); + BLI_assert(slen < sizeof(patch)); + return patch; +} + +char *GLShader::glsl_patch_get(GLenum gl_stage) +{ + if (gl_stage == GL_COMPUTE_SHADER) { + return glsl_patch_compute_get(); + } + return glsl_patch_default_get(); +} + /* Create, compile and attach the shader stage to the shader program. */ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources) { @@ -121,7 +147,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> } /* Patch the shader code using the first source slot. */ - sources[0] = glsl_patch_get(); + sources[0] = glsl_patch_get(gl_stage); glShaderSource(shader, sources.size(), sources.data(), nullptr); glCompileShader(shader); @@ -142,6 +168,9 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> case GL_FRAGMENT_SHADER: this->print_log(sources, log, "FragShader", !status); break; + case GL_COMPUTE_SHADER: + this->print_log(sources, log, "ComputeShader", !status); + break; } } } @@ -172,6 +201,11 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources) frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources); } +void GLShader::compute_shader_from_glsl(MutableSpan<const char *> sources) +{ + compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources); +} + bool GLShader::finalize() { if (compilation_failed_) { diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index 152eb2f068a..48aaaf2283d 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -43,6 +43,7 @@ class GLShader : public Shader { GLuint vert_shader_ = 0; GLuint geom_shader_ = 0; GLuint frag_shader_ = 0; + GLuint compute_shader_ = 0; /** True if any shader failed to compile. */ bool compilation_failed_ = false; @@ -56,6 +57,7 @@ class GLShader : public Shader { void vertex_shader_from_glsl(MutableSpan<const char *> sources) override; void geometry_shader_from_glsl(MutableSpan<const char *> sources) override; void fragment_shader_from_glsl(MutableSpan<const char *> sources) override; + void compute_shader_from_glsl(MutableSpan<const char *> sources) override; bool finalize(void) override; void transform_feedback_names_set(Span<const char *> name_list, @@ -75,7 +77,7 @@ class GLShader : public Shader { int program_handle_get(void) const override; private: - char *glsl_patch_get(void); + char *glsl_patch_get(GLenum gl_stage); GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources); diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 5870c645bf4..9cf072b2e8a 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -29,6 +29,8 @@ #include "gl_shader_interface.hh" +#include "GPU_capabilities.h" + namespace blender::gpu { /* -------------------------------------------------------------------- */ @@ -125,6 +127,18 @@ static inline int image_binding(int32_t program, return -1; } } + +static inline int ssbo_binding(int32_t program, uint32_t ssbo_index) +{ + GLint binding = -1; + GLenum property = GL_BUFFER_BINDING; + GLint values_written = 0; + glGetProgramResourceiv( + program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding); + + return binding; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -149,6 +163,13 @@ GLShaderInterface::GLShaderInterface(GLuint program) glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); uniform_len = active_uniform_len; + GLint max_ssbo_name_len = 0, ssbo_len = 0; + if (GPU_shader_storage_buffer_objects_support()) { + glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len); + glGetProgramInterfaceiv( + program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len); + } + BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t"); /* Work around driver bug with Intel HD 4600 on Windows 7/8, where @@ -162,6 +183,9 @@ GLShaderInterface::GLShaderInterface(GLuint program) if (uniform_len > 0 && max_uniform_name_len == 0) { max_uniform_name_len = 256; } + if (ssbo_len > 0 && max_ssbo_name_len == 0) { + max_ssbo_name_len = 256; + } /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before * allocating the uniform array. */ @@ -186,11 +210,12 @@ GLShaderInterface::GLShaderInterface(GLuint program) } MEM_freeN(ubo_uni_ids); - int input_tot_len = attr_len + ubo_len + uniform_len; + int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_len; inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + - uniform_len * max_uniform_name_len; + uniform_len * max_uniform_name_len + + ssbo_len * max_ssbo_name_len; name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); uint32_t name_buffer_offset = 0; @@ -257,6 +282,22 @@ GLShaderInterface::GLShaderInterface(GLuint program) } } + /* SSBOs */ + for (int i = 0; i < ssbo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + glGetProgramResourceName( + program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name); + + const GLint binding = ssbo_binding(program, i); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++]; + input->binding = input->location = binding; + + name_buffer_offset += this->set_input_name(input, name, name_len); + } + /* Builtin Uniforms */ for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index 651c3c22afa..3b4b40b1d10 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -121,6 +121,9 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits) if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { barrier |= GL_TEXTURE_FETCH_BARRIER_BIT; } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { + barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + } return barrier; } diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index b65686165d9..e2478a9976c 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -368,7 +368,7 @@ void GLTexture::copy_to(Texture *dst_) void *GLTexture::read(int mip, eGPUDataFormat type) { BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED)); - BLI_assert(mip <= mipmaps_); + BLI_assert(mip <= mipmaps_ || mip == 0); BLI_assert(validate_data_format(format_, type)); /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */ diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc index a56d5269fde..ce16a491528 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.cc +++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc @@ -29,6 +29,10 @@ namespace blender::gpu { void GLVertBuf::acquire_data() { + if (usage_ == GPU_USAGE_DEVICE_ONLY) { + return; + } + /* Discard previous data if any. */ MEM_SAFE_FREE(data); data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__); @@ -36,6 +40,10 @@ void GLVertBuf::acquire_data() void GLVertBuf::resize_data() { + if (usage_ == GPU_USAGE_DEVICE_ONLY) { + return; + } + data = (uchar *)MEM_reallocN(data, sizeof(uchar) * this->size_alloc_get()); } @@ -94,8 +102,10 @@ void GLVertBuf::bind() vbo_size_ = this->size_used_get(); /* Orphan the vbo to avoid sync then upload data. */ glBufferData(GL_ARRAY_BUFFER, vbo_size_, nullptr, to_gl(usage_)); - glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data); - + /* Do not transfer data from host to device when buffer is device only. */ + if (usage_ != GPU_USAGE_DEVICE_ONLY) { + glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data); + } memory_usage += vbo_size_; if (usage_ == GPU_USAGE_STATIC) { @@ -106,6 +116,37 @@ void GLVertBuf::bind() } } +void GLVertBuf::bind_as_ssbo(uint binding) +{ + bind(); + BLI_assert(vbo_id_ != 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_); +} + +const void *GLVertBuf::read() const +{ + BLI_assert(is_active()); + void *result = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + return result; +} + +void *GLVertBuf::unmap(const void *mapped_data) const +{ + void *result = MEM_mallocN(vbo_size_, __func__); + memcpy(result, mapped_data, vbo_size_); + return result; +} + +bool GLVertBuf::is_active() const +{ + if (!vbo_id_) { + return false; + } + int active_vbo_id = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &active_vbo_id); + return vbo_id_ == active_vbo_id; +} + void GLVertBuf::update_sub(uint start, uint len, void *data) { glBufferSubData(GL_ARRAY_BUFFER, start, len, data); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh index e2bf6cd00e8..6c38a2225b3 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.hh +++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh @@ -47,12 +47,19 @@ class GLVertBuf : public VertBuf { void update_sub(uint start, uint len, void *data) override; + const void *read() const override; + void *unmap(const void *mapped_data) const override; + protected: void acquire_data(void) override; void resize_data(void) override; void release_data(void) override; void upload_data(void) override; void duplicate_data(VertBuf *dst) override; + void bind_as_ssbo(uint binding) override; + + private: + bool is_active() const; MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf"); }; @@ -65,6 +72,7 @@ static inline GLenum to_gl(GPUUsageType type) case GPU_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; case GPU_USAGE_STATIC: + case GPU_USAGE_DEVICE_ONLY: return GL_STATIC_DRAW; default: BLI_assert(0); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 60ed098beb3..4ad5d4232de 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -150,3 +150,9 @@ void vector_math_faceforward( { outVector = faceforward(a, b, c); } + +void vector_math_multiply_add( + vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) +{ + outVector = a * b + c; +} diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc new file mode 100644 index 00000000000..e8645b89e41 --- /dev/null +++ b/source/blender/gpu/tests/gpu_shader_test.cc @@ -0,0 +1,301 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "GPU_capabilities.h" +#include "GPU_compute.h" +#include "GPU_index_buffer.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" +#include "GPU_vertex_format.h" + +#include "MEM_guardedalloc.h" + +#include "gpu_testing.hh" + +#include "GPU_glew.h" + +namespace blender::gpu::tests { + +TEST_F(GPUTest, gpu_shader_compute_2d) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 512; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1, local_size_y = 1) in; +layout(rgba32f, binding = 0) uniform image2D img_output; + +void main() { + vec4 pixel = vec4(1.0, 0.5, 0.2, 1.0); + imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel); +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_2d"); + EXPECT_NE(shader, nullptr); + + /* Create texture to store result and attach to shader. */ + GPUTexture *texture = GPU_texture_create_2d( + "gpu_shader_compute_2d", SIZE, SIZE, 0, GPU_RGBA32F, nullptr); + EXPECT_NE(texture, nullptr); + + GPU_shader_bind(shader); + GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "img_output")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, SIZE, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0)); + EXPECT_NE(data, nullptr); + for (int index = 0; index < SIZE * SIZE; index++) { + EXPECT_FLOAT_EQ(data[index * 4 + 0], 1.0f); + EXPECT_FLOAT_EQ(data[index * 4 + 1], 0.5f); + EXPECT_FLOAT_EQ(data[index * 4 + 2], 0.2f); + EXPECT_FLOAT_EQ(data[index * 4 + 3], 1.0f); + } + MEM_freeN(data); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_texture_unbind(texture); + GPU_texture_free(texture); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_compute_1d) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 10; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(rgba32f, binding = 1) uniform image1D outputVboData; + +void main() { + int index = int(gl_GlobalInvocationID.x); + vec4 pos = vec4(gl_GlobalInvocationID.x); + imageStore(outputVboData, index, pos); +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_1d"); + EXPECT_NE(shader, nullptr); + + /* Construct Texture. */ + GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL); + EXPECT_NE(texture, nullptr); + + GPU_shader_bind(shader); + GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "outputVboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + + /* Create texture to load back result. */ + float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0)); + EXPECT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + float expected_value = index; + EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value); + } + MEM_freeN(data); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_texture_unbind(texture); + GPU_texture_free(texture); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_compute_vbo) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 128; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer outputVboData +{ + vec4 out_positions[]; +}; + +void main() { + uint index = gl_GlobalInvocationID.x; + vec4 pos = vec4(gl_GlobalInvocationID.x); + out_positions[index] = pos; +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + /* Construct VBO. */ + static GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_DEVICE_ONLY); + GPU_vertbuf_data_alloc(vbo, SIZE); + GPU_vertbuf_bind_as_ssbo(vbo, GPU_shader_get_ssbo(shader, "outputVboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + /* Download the vertex buffer. */ + const float *data = static_cast<const float *>(GPU_vertbuf_read(vbo)); + ASSERT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + float expected_value = index; + EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value); + } + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_vertbuf_discard(vbo); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_compute_ibo) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 128; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer outputIboData +{ + uint out_indexes[]; +}; + +void main() { + uint store_index = int(gl_GlobalInvocationID.x); + out_indexes[store_index] = store_index; +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + /* Construct IBO. */ + GPUIndexBuf *ibo = GPU_indexbuf_build_on_device(SIZE); + GPU_indexbuf_bind_as_ssbo(ibo, GPU_shader_get_ssbo(shader, "outputIboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + /* Download the index buffer. */ + const uint32_t *data = GPU_indexbuf_read(ibo); + ASSERT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + uint32_t expected = index; + EXPECT_EQ(data[index], expected); + } + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_indexbuf_discard(ibo); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_ssbo_binding) +{ + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer ssboBinding0 +{ + int data0[]; +}; +layout(std430, binding = 1) buffer ssboBinding1 +{ + int data1[]; +}; + +void main() { +} + +)"; + + GPUShader *shader = GPU_shader_create_compute(compute_glsl, nullptr, nullptr, "gpu_shader_ssbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + EXPECT_EQ(0, GPU_shader_get_ssbo(shader, "ssboBinding0")); + EXPECT_EQ(1, GPU_shader_get_ssbo(shader, "ssboBinding1")); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_shader_free(shader); +} + +} // namespace blender::gpu::tests diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index d131e4dacdc..9c84127105a 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -380,8 +380,6 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); */ int ismovie(const char *filepath); -void IMB_anim_set_preseek(struct anim *anim, int preseek); -int IMB_anim_get_preseek(struct anim *anim); int IMB_anim_get_image_width(struct anim *anim); int IMB_anim_get_image_height(struct anim *anim); diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index 7d7864306a1..cfeffcca0ea 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -87,7 +87,7 @@ struct anim_index; struct anim { int ib_flags; int curtype; - int curposition; /* index 0 = 1e, 1 = 2e, enz. */ + int cur_position; /* index 0 = 1e, 1 = 2e, enz. */ int duration_in_frames; int frs_sec; double frs_sec_base; @@ -105,7 +105,6 @@ struct anim { int orientation; size_t framesize; int interlacing; - int preseek; int streamindex; /* avi */ @@ -132,10 +131,10 @@ struct anim { struct SwsContext *img_convert_ctx; int videoStream; - struct ImBuf *last_frame; - int64_t last_pts; - int64_t next_pts; - AVPacket *next_packet; + struct ImBuf *cur_frame_final; + int64_t cur_pts; + int64_t cur_key_frame_pts; + AVPacket *cur_packet; #endif char index_dir[768]; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 5918a4bf16e..622b6cbfc16 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -433,8 +433,7 @@ static int startavi(struct anim *anim) anim->orientation = 0; anim->framesize = anim->x * anim->y * 4; - anim->curposition = 0; - anim->preseek = 0; + anim->cur_position = 0; # if 0 printf("x:%d y:%d size:%d interl:%d dur:%d\n", @@ -650,12 +649,12 @@ static int startffmpeg(struct anim *anim) anim->orientation = 0; anim->framesize = anim->x * anim->y * 4; - anim->curposition = -1; - anim->last_frame = 0; - anim->last_pts = -1; - anim->next_pts = -1; - anim->next_packet = av_packet_alloc(); - anim->next_packet->stream_index = -1; + anim->cur_position = -1; + anim->cur_frame_final = 0; + anim->cur_pts = -1; + anim->cur_key_frame_pts = -1; + anim->cur_packet = av_packet_alloc(); + anim->cur_packet->stream_index = -1; anim->pFrame = av_frame_alloc(); anim->pFrameComplete = false; @@ -671,7 +670,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "Could not allocate frame data.\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -684,7 +683,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -706,20 +705,13 @@ static int startffmpeg(struct anim *anim) 1); } - if (pCodecCtx->has_b_frames) { - anim->preseek = 25; /* FIXME: detect gopsize ... */ - } - else { - anim->preseek = 0; - } - anim->img_convert_ctx = sws_getContext(anim->x, anim->y, anim->pCodecCtx->pix_fmt, anim->x, anim->y, AV_PIX_FMT_RGBA, - SWS_FAST_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT, + SWS_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); @@ -728,7 +720,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "Can't transform color space??? Bailing out...\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -769,13 +761,13 @@ static int startffmpeg(struct anim *anim) /* postprocess the image in anim->pFrame and do color conversion * and deinterlacing stuff. * - * Output is anim->last_frame + * Output is anim->cur_frame_final */ static void ffmpeg_postprocess(struct anim *anim) { AVFrame *input = anim->pFrame; - ImBuf *ibuf = anim->last_frame; + ImBuf *ibuf = anim->cur_frame_final; int filter_y = 0; if (!anim->pFrameComplete) { @@ -902,7 +894,7 @@ static void ffmpeg_postprocess(struct anim *anim) } } -/* decode one video frame also considering the packet read into next_packet */ +/* decode one video frame also considering the packet read into cur_packet */ static int ffmpeg_decode_video_frame(struct anim *anim) { @@ -910,40 +902,43 @@ static int ffmpeg_decode_video_frame(struct anim *anim) av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); - if (anim->next_packet->stream_index == anim->videoStream) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + if (anim->cur_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) { + while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", - (anim->next_packet->stream_index == anim->videoStream) ? "->" : " ", - anim->next_packet->stream_index, + (anim->cur_packet->stream_index == anim->videoStream) ? "->" : " ", + anim->cur_packet->stream_index, anim->videoStream, - (anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts, - (anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts, - (anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->next_packet->stream_index == anim->videoStream) { + (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) { anim->pFrameComplete = 0; - avcodec_send_packet(anim->pCodecCtx, anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; + } av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->next_pts); + (int64_t)anim->cur_pts); break; } } - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } if (rval == AVERROR_EOF) { @@ -954,20 +949,23 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; + } av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->next_pts); + (int64_t)anim->cur_pts); rval = 0; } } if (rval < 0) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; av_log(anim->pFormatCtx, AV_LOG_ERROR, @@ -1021,6 +1019,35 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) return false; } +static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position) +{ + AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; + double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); + int64_t st_time = anim->pFormatCtx->start_time; + int64_t pos = (int64_t)(position)*AV_TIME_BASE; + /* Step back half a time base position to make sure that we get the requested + * frame and not the one after it. + */ + pos -= (AV_TIME_BASE / 2); + pos /= frame_rate; + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", + pos, + (st_time != AV_NOPTS_VALUE) ? st_time : 0); + + if (pos < 0) { + pos = 0; + } + + if (st_time != AV_NOPTS_VALUE) { + pos += st_time; + } + + return pos; +} + static int64_t ffmpeg_get_pts_to_search(struct anim *anim, struct anim_index *tc_index, int position) @@ -1045,77 +1072,60 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, return pts_to_search; } +/* Check if the pts will get us the same frame that we already have in memory from last decode. */ static bool ffmpeg_pts_matches_last_frame(struct anim *anim, int64_t pts_to_search) { - return anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search; -} - -/* Requested video frame is expected to be found within different GOP as last decoded frame. - * Seeking to new position and scanning is fastest way to get requested frame. - * Check whether ffmpeg_can_scan() and ffmpeg_pts_matches_last_frame() is false before using this - * function. */ -static bool ffmpeg_can_seek(struct anim *anim, int position) -{ - return position != anim->curposition + 1; -} - -/* Requested video frame is expected to be found within same GOP as last decoded frame. - * Decoding frames in sequence until frame matches requested one is fastest way to get it. */ -static bool ffmpeg_can_scan(struct anim *anim, int position, struct anim_index *tc_index) -{ - if (position > anim->curposition + 1 && anim->preseek && !tc_index && - position - (anim->curposition + 1) < anim->preseek) { - return true; - } - - if (tc_index == NULL) { - return false; + if (anim->pFrame && anim->cur_frame_final) { + return labs(anim->cur_pts - pts_to_search) < anim->pFrame->pkt_duration; } - int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); - int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition); - return IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index); + return false; } static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position) { - return position == 0 && anim->curposition == -1; + return position == 0 && anim->cur_position == -1; } /* Decode frames one by one until its PTS matches pts_to_search. */ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search) { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval\n"); - - /* there seem to exist *very* silly GOP lengths out in the wild... */ - int count = 1000; + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n"); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); - while (count > 0 && anim->next_pts < pts_to_search) { + int64_t start_gop_frame = anim->cur_key_frame_pts; + + while (anim->cur_pts < pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); if (!ffmpeg_decode_video_frame(anim)) { break; } - count--; + + if (start_gop_frame != anim->cur_key_frame_pts) { + break; + } } - if (count == 0) { + + if (start_gop_frame != anim->cur_key_frame_pts) { + /* We went into an other GOP frame. This should never happen as we should have positioned us + * correctly by seeking into the GOP frame that contains the frame we want. */ av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN failed: completely lost in stream, " "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); } - if (anim->next_pts == pts_to_search) { + if (anim->cur_pts == pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n"); } else { @@ -1123,22 +1133,23 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea } } -/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or read_seek2() - * functions defined. When seeking in these formats, rule to seek to last necessary I-frame is not - * honored. It is not even guaranteed that I-frame, that must be decoded will be read. See - * https://trac.ffmpeg.org/ticket/1607 and https://developer.blender.org/T86944. */ -static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_pos) +/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or + * read_seek2() functions defined. When seeking in these formats, rule to seek to last + * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be + * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and + * https://developer.blender.org/T86944. */ +static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t *requested_pos) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t current_pos = requested_pos; + int64_t current_pos = *requested_pos; /* This time offset maximum limit is arbitrary. If some files fails to decode it may be - * increased. Seek performance will be negatively affected. Small initial offset is necessary - * because encoder can re-arrange frames as it needs but within it's delay, which is usually - * small. */ + * increased. Seek performance will be negatively affected. Small initial offset is + * necessary because encoder can re-arrange frames as it needs but within it's delay, which + * is usually small. */ for (int offset = 5; offset < 25; offset++) { - current_pos = requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); + current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); current_pos = max_ii(current_pos, 0); /* Seek to timestamp. */ @@ -1147,42 +1158,51 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p } /* Read first video stream packet. */ - AVPacket read_packet = {0}; - while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) { - if (anim->next_packet->stream_index == anim->videoStream) { + AVPacket *read_packet = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) { + if (anim->cur_packet->stream_index == anim->videoStream) { break; } } /* If this packet contains I-frame, exit loop. This should be the frame that we need. */ - if (read_packet.flags & AV_PKT_FLAG_KEY) { + bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY; + av_packet_free(&read_packet); + if (is_key_frame) { break; } } + *requested_pos = current_pos; + /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */ return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD); } -/* Seek to last necessary I-frame and scan-decode until requested frame is found. */ -static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_index *tc_index) +/* Seek to last necessary key frame. */ +static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t st_time = anim->pFormatCtx->start_time; - - int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); int64_t pos; int ret; if (tc_index) { + /* We can use timestamps generated from our indexer to seek. */ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); + int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position); + + if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) { + /* No need to seek, return early. */ + return 0; + } uint64_t dts; pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index); + anim->cur_key_frame_pts = pos; + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts); @@ -1198,22 +1218,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ } } else { - pos = (int64_t)(position)*AV_TIME_BASE / frame_rate; - - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", - pos, - (st_time != AV_NOPTS_VALUE) ? st_time : 0); - - if (pos < 0) { - pos = 0; - } - - if (st_time != AV_NOPTS_VALUE) { - pos += st_time; - } - + /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from. + */ + pos = ffmpeg_get_seek_pos(anim, position); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); AVFormatContext *format_ctx = anim->pFormatCtx; @@ -1222,11 +1229,44 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } else { - ret = ffmpeg_generic_seek_workaround(anim, pos); + ret = ffmpeg_generic_seek_workaround(anim, &pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", 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); + } + bool same_gop = current_gop_start_packet->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. */ + AVPacket *temp = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, temp) >= 0) { + if (temp->stream_index == anim->videoStream && temp->pts == anim->cur_packet->pts) { + break; + } + av_packet_unref(temp); + } + av_packet_free(¤t_gop_start_packet); + av_packet_free(&temp); + return 0; + } + + anim->cur_key_frame_pts = current_gop_start_packet->pts; + av_packet_free(¤t_gop_start_packet); + /* Seek back so we are at the correct position after we decoded a frame. */ + av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } } if (ret < 0) { + int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); av_log(anim->pFormatCtx, AV_LOG_ERROR, "FETCH: " @@ -1234,25 +1274,19 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ "): errcode = %d\n", pos, position, - (int64_t)pts_to_search, + pts_to_search, ret); } avcodec_flush_buffers(anim->pCodecCtx); - anim->next_pts = -1; + anim->cur_pts = -1; - if (anim->next_packet->stream_index == anim->videoStream) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; - } - - /* memset(anim->pFrame, ...) ?? */ - if (ret < 0) { - /* Seek failed. */ - return; + if (anim->cur_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } - ffmpeg_decode_video_frame_scan(anim, pts_to_search); + return ret; } static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc) @@ -1282,25 +1316,22 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, - "FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n", - (int64_t)anim->last_pts, - (int64_t)anim->next_pts); - IMB_refImBuf(anim->last_frame); - anim->curposition = position; - return anim->last_frame; + "FETCH: frame repeat: pts: %" PRId64 "\n", + (int64_t)anim->cur_pts); + IMB_refImBuf(anim->cur_frame_final); + anim->cur_position = position; + return anim->cur_frame_final; } - if (ffmpeg_can_scan(anim, position, tc_index) || ffmpeg_is_first_frame_decode(anim, position)) { - ffmpeg_decode_video_frame_scan(anim, pts_to_search); - } - else if (ffmpeg_can_seek(anim, position)) { - ffmpeg_seek_and_decode(anim, position, tc_index); - } - else { + if (position == anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim, position)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n"); + ffmpeg_decode_video_frame(anim); + } + else if (ffmpeg_seek_to_key_frame(anim, position, tc_index) >= 0) { + ffmpeg_decode_video_frame_scan(anim, pts_to_search); } - IMB_freeImBuf(anim->last_frame); + IMB_freeImBuf(anim->cur_frame_final); /* Certain versions of FFmpeg have a bug in libswscale which ends up in crash * when destination buffer is not properly aligned. For example, this happens @@ -1320,23 +1351,20 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ * * The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker * and is fixed in the newer versions than 4.3.1. */ - anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0); - anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); - anim->last_frame->mall |= IB_rect; + anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0); + anim->cur_frame_final->rect = MEM_mallocN_aligned( + (size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); + anim->cur_frame_final->mall |= IB_rect; - anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); + anim->cur_frame_final->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); ffmpeg_postprocess(anim); - anim->last_pts = anim->next_pts; - - ffmpeg_decode_video_frame(anim); + anim->cur_position = position; - anim->curposition = position; + IMB_refImBuf(anim->cur_frame_final); - IMB_refImBuf(anim->last_frame); - - return anim->last_frame; + return anim->cur_frame_final; } static void free_anim_ffmpeg(struct anim *anim) @@ -1348,7 +1376,7 @@ static void free_anim_ffmpeg(struct anim *anim) if (anim->pCodecCtx) { avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrame); @@ -1369,7 +1397,7 @@ static void free_anim_ffmpeg(struct anim *anim) av_frame_free(&anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); - IMB_freeImBuf(anim->last_frame); + IMB_freeImBuf(anim->cur_frame_final); } anim->duration_in_frames = 0; } @@ -1503,13 +1531,13 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, an_stringenc(anim->name, head, tail, digits, pic); ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } break; case ANIM_MOVIE: ibuf = movie_fetchibuf(anim, position); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; IMB_convert_rgba_to_abgr(ibuf); } break; @@ -1517,7 +1545,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, case ANIM_AVI: ibuf = avi_fetchibuf(anim, position); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } break; #endif @@ -1525,7 +1553,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, case ANIM_FFMPEG: ibuf = ffmpeg_fetchibuf(anim, position, tc); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } filter_y = 0; /* done internally */ break; @@ -1536,7 +1564,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, if (filter_y) { IMB_filtery(ibuf); } - BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1); + BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->cur_position + 1); } return ibuf; } @@ -1591,16 +1619,6 @@ bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bo return false; } -void IMB_anim_set_preseek(struct anim *anim, int preseek) -{ - anim->preseek = preseek; -} - -int IMB_anim_get_preseek(struct anim *anim) -{ - return anim->preseek; -} - int IMB_anim_get_image_width(struct anim *anim) { return anim->x; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 11ce77e3091..46b28086129 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -466,13 +466,6 @@ struct proxy_output_ctx { struct anim *anim; }; -// work around stupid swscaler 16 bytes alignment bug... - -static int round_up(int x, int mod) -{ - return x + ((mod - (x % mod)) % mod); -} - static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality) { @@ -499,13 +492,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c = avcodec_alloc_context3(NULL); rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_H264; - rv->c->width = width; - rv->c->height = height; - rv->c->gop_size = 10; - rv->c->max_b_frames = 0; - /* Correct wrong default ffmpeg param which crash x264. */ - rv->c->qmin = 10; - rv->c->qmax = 51; rv->of->oformat->video_codec = rv->c->codec_id; rv->codec = avcodec_find_encoder(rv->c->codec_id); @@ -520,6 +506,13 @@ 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; + rv->c->max_b_frames = 0; + if (rv->codec->pix_fmts) { rv->c->pix_fmt = rv->codec->pix_fmts[0]; } @@ -533,8 +526,8 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c->time_base.num = 1; rv->st->time_base = rv->c->time_base; - /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to - * highest quality. */ + /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality, + * `crf_range_max` to highest quality. */ const int crf_range_min = 32; const int crf_range_max = 17; int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min); @@ -542,8 +535,11 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( AVDictionary *codec_opts = NULL; /* High quality preset value. */ av_dict_set_int(&codec_opts, "crf", crf, 0); - /* Prefer smaller file-size. */ - av_dict_set(&codec_opts, "preset", "slow", 0); + /* Prefer smaller file-size. Presets from `veryslow` to `veryfast` produce output with very + * similar file-size, but there is big difference in performance. + * In some cases `veryfast` preset will produce smallest file-size. */ + av_dict_set(&codec_opts, "preset", "veryfast", 0); + av_dict_set(&codec_opts, "tune", "fastdecode", 0); if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { rv->c->thread_count = 0; @@ -595,15 +591,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( if (st->codecpar->width != width || st->codecpar->height != height || st->codecpar->format != rv->c->pix_fmt) { rv->frame = av_frame_alloc(); - av_image_fill_arrays( - rv->frame->data, - rv->frame->linesize, - MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1), - "alloc proxy output frame"), - rv->c->pix_fmt, - round_up(width, 16), - height, - 1); + + av_image_fill_arrays(rv->frame->data, + rv->frame->linesize, + MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1), + "alloc proxy output frame"), + rv->c->pix_fmt, + width, + height, + 1); + + rv->frame->format = rv->c->pix_fmt; + rv->frame->width = width; + rv->frame->height = height; rv->sws_ctx = sws_getContext(st->codecpar->width, rv->orig_height, @@ -686,6 +686,9 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr packet->stream_index = ctx->st->index; av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(ctx->of, ctx->st, packet); +# endif int write_ret = av_interleaved_write_frame(ctx->of, packet); if (write_ret != 0) { diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 440375f60dc..48b5b0c34db 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -516,7 +516,8 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) * The first "Blender" is a simple identify to help * in the read process. */ - text_len = BLI_snprintf(text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop)); + text_len = BLI_snprintf_rlen( + text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop)); jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)text, text_len + 1); /* TODO(sergey): Ideally we will try to re-use allocation as diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index e79a2bc98ff..a2c1b8f5af6 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -41,6 +41,7 @@ #include "BKE_gpencil_geom.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_scene.h" #include "UI_view2d.h" @@ -69,18 +70,21 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams) cfra_ = iparams->frame_cur; /* Calculate camera matrix. */ - prepare_camera_params(iparams); + prepare_camera_params(scene_, iparams); } -void GpencilIO::prepare_camera_params(const GpencilIOParams *iparams) +void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *iparams) { params_ = *iparams; const bool is_pdf = params_.mode == GP_EXPORT_TO_PDF; const bool any_camera = (params_.v3d->camera != nullptr); const bool force_camera_view = is_pdf && any_camera; + /* Ensure camera switch is applied. */ + BKE_scene_camera_switch_update(scene); + /* Calculate camera matrix. */ - Object *cam_ob = params_.v3d->camera; + Object *cam_ob = scene->camera; if (cam_ob != nullptr) { /* Set up parameters. */ CameraParams params; diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh index 2e1e1707c78..c3c6f1156bb 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh @@ -50,7 +50,7 @@ class GpencilIO { GpencilIO(const GpencilIOParams *iparams); void frame_number_set(const int value); - void prepare_camera_params(const GpencilIOParams *iparams); + void prepare_camera_params(Scene *scene, const GpencilIOParams *iparams); protected: GpencilIOParams params_; diff --git a/source/blender/io/gpencil/intern/gpencil_io_capi.cc b/source/blender/io/gpencil/intern/gpencil_io_capi.cc index 8093ec3c52d..544c51e0b4f 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_capi.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_capi.cc @@ -121,7 +121,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph, CFRA = i; BKE_scene_graph_update_for_newframe(depsgraph); - exporter->prepare_camera_params(iparams); + exporter->prepare_camera_params(scene, iparams); exporter->frame_number_set(i); exporter->add_newpage(); exporter->add_body(); @@ -130,10 +130,11 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph, /* Back to original frame. */ exporter->frame_number_set(iparams->frame_cur); CFRA = iparams->frame_cur; + BKE_scene_camera_switch_update(scene); BKE_scene_graph_update_for_newframe(depsgraph); } else { - exporter->prepare_camera_params(iparams); + exporter->prepare_camera_params(scene, iparams); exporter->add_newpage(); exporter->add_body(); result = exporter->write(); @@ -146,6 +147,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph, /* Export current frame in SVG. */ #ifdef WITH_PUGIXML static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter, + Scene *scene, const GpencilIOParams *iparams, const bool newpage, const bool body, @@ -153,7 +155,7 @@ static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter, { bool result = false; exporter->frame_number_set(iparams->frame_cur); - exporter->prepare_camera_params(iparams); + exporter->prepare_camera_params(scene, iparams); if (newpage) { result |= exporter->add_newpage(); @@ -189,7 +191,7 @@ bool gpencil_io_export(const char *filename, GpencilIOParams *iparams) #ifdef WITH_PUGIXML case GP_EXPORT_TO_SVG: { GpencilExporterSVG exporter = GpencilExporterSVG(filename, iparams); - return gpencil_io_export_frame_svg(&exporter, iparams, true, true, true); + return gpencil_io_export_frame_svg(&exporter, scene_, iparams, true, true, true); break; } #endif diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 8bf9afafa1b..dd262f78f6b 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -366,7 +366,7 @@ typedef struct Library { struct PackedFile *packedfile; - /* Temp data needed by read/write code. */ + /* Temp data needed by read/write code, and liboverride recursive resync. */ int temp_index; /** See BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION, needed for do_versions. */ short versionfile, subversionfile; diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 07fbf263d80..a94bc4625df 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -534,6 +534,8 @@ typedef struct bRotLimitConstraint { float zmin, zmax; short flag; short flag2; + char euler_order; + char _pad[3]; } bRotLimitConstraint; /* Limit Scale Constraint */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 716c480bab8..f6242679808 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -43,6 +43,7 @@ struct Key; struct Material; struct Object; struct VFont; +struct CurveEval; /* These two Lines with # tell makesdna this struct can be excluded. */ # @@ -300,6 +301,12 @@ typedef struct Curve { char _pad2[6]; float fsize_realtime; + /** + * A pointer to curve data from geometry nodes, currently only set for evaluated + * objects by the dependency graph iterator, and owned by #geometry_set_eval. + */ + struct CurveEval *curve_eval; + void *batch_cache; } Curve; diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 0acf979516e..ea3c1ff7275 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -560,6 +560,8 @@ typedef enum eGPDlayer_Flag { GP_LAYER_USE_MASK = (1 << 13), /*TODO: DEPRECATED */ /* Ruler Layer */ GP_LAYER_IS_RULER = (1 << 14), + /* Disable masks in viewlayer render */ + GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER = (1 << 15), } eGPDlayer_Flag; /** #bGPDlayer.onion_flag */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 58c94b6f369..1ab6c5e52a6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -461,7 +461,6 @@ typedef struct bNodeTree { short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ short done; - char _pad2[4]; /** Specific node type this tree is used for. */ int nodetype DNA_DEPRECATED; @@ -472,6 +471,8 @@ typedef struct bNodeTree { short render_quality; /** Tile size for compositor engine. */ int chunksize; + /** Execution mode to use for compositor engine. */ + int execution_mode; rctf viewer_border; @@ -545,6 +546,12 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; +/* tree->execution_mode */ +typedef enum eNodeTreeExecutionMode { + NTREE_EXECUTION_MODE_TILED = 0, + NTREE_EXECUTION_MODE_FULL_FRAME = 1, +} eNodeTreeExecutionMode; + /* socket value structs for input buttons * DEPRECATED now using ID properties */ @@ -1626,6 +1633,7 @@ typedef enum NodeVectorMathOperation { NODE_VECTOR_MATH_TANGENT = 23, NODE_VECTOR_MATH_REFRACT = 24, NODE_VECTOR_MATH_FACEFORWARD = 25, + NODE_VECTOR_MATH_MULTIPLY_ADD = 26, } NodeVectorMathOperation; /* Boolean math node operations. */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index aa1178fb139..9951bdefbbb 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -144,6 +144,9 @@ typedef struct Object_Runtime { */ char is_data_eval_owned; + /** Start time of the mode transfer overlay animation. */ + double overlay_mode_transfer_start_time; + /** Axis aligned boundbox (in localspace). */ struct BoundBox *bb; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 4b95dd41b30..7e0bf81457d 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -57,6 +57,7 @@ typedef struct StripElem { char name[256]; /** Ignore when zeroed. */ int orig_width, orig_height; + float orig_fps; } StripElem; typedef struct StripCrop { @@ -172,7 +173,7 @@ typedef struct Sequence { float sat; float mul, handsize; - short anim_preseek; + short anim_preseek; /* UNUSED. */ /** Streamindex for movie or sound files with several streams. */ short streamindex; /** For multicam source selection. */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1fed8e14bd6..61d2c04d98b 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -642,11 +642,12 @@ typedef struct UserDef_Experimental { * when the release cycle is not alpha. */ char use_new_hair_type; char use_new_point_cloud_type; + char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; char use_asset_browser; char use_override_templates; - char _pad[6]; + char _pad[5]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h index 10d0bafec61..9dfc37e57b1 100644 --- a/source/blender/makesdna/DNA_view3d_defaults.h +++ b/source/blender/makesdna/DNA_view3d_defaults.h @@ -70,6 +70,7 @@ \ .gpencil_paper_opacity = 0.5f, \ .gpencil_grid_opacity = 0.9f, \ + .gpencil_vertex_paint_opacity = 1.0f, \ } #define _DNA_DEFAULT_View3DCursor \ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 2f4e4e57b9f..9e7e30d913e 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -515,6 +515,7 @@ enum { V3D_OVERLAY_HIDE_OBJECT_ORIGINS = (1 << 10), V3D_OVERLAY_STATS = (1 << 11), V3D_OVERLAY_FADE_INACTIVE = (1 << 12), + V3D_OVERLAY_MODE_TRANSFER = (1 << 13), }; /** #View3DOverlay.edit_flag */ diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 73b6f92184c..2d6b689b88d 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -699,9 +699,29 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) if (remap_local_usages) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } + + WM_main_add_notifier(NC_ID | NA_ADDED, NULL); + return local_id; } +static ID *rna_ID_override_hierarchy_create( + ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference) +{ + if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { + return NULL; + } + + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + + ID *id_root_override = NULL; + BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override); + + WM_main_add_notifier(NC_ID | NA_ADDED, NULL); + + return id_root_override; +} + static void rna_ID_override_template_create(ID *id, ReportList *reports) { if (!U.experimental.use_override_templates) { @@ -1228,7 +1248,7 @@ static void rna_ImagePreview_icon_reload(PreviewImage *prv) static PointerRNA rna_IDPreview_get(PointerRNA *ptr) { ID *id = (ID *)ptr->data; - PreviewImage *prv_img = BKE_previewimg_id_ensure(id); + PreviewImage *prv_img = BKE_previewimg_id_get(id); return rna_pointer_inherit_refine(ptr, &RNA_ImagePreview, prv_img); } @@ -1798,12 +1818,12 @@ static void rna_def_ID(BlenderRNA *brna) srna, "override_library", "IDOverrideLibrary", "Library Override", "Library override data"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - prop = RNA_def_pointer( - srna, - "preview", - "ImagePreview", - "Preview", - "Preview image and icon of this data-block (None if not supported for this type of data)"); + prop = RNA_def_pointer(srna, + "preview", + "ImagePreview", + "Preview", + "Preview image and icon of this data-block (always None if not supported " + "for this type of data)"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_pointer_funcs(prop, "rna_IDPreview_get", NULL, NULL, NULL); @@ -1851,6 +1871,30 @@ static void rna_def_ID(BlenderRNA *brna) "Whether local usages of the linked ID should be remapped to the new " "library override of it"); + func = RNA_def_function(srna, "override_hierarchy_create", "rna_ID_override_hierarchy_create"); + RNA_def_function_ui_description( + func, + "Create an overridden local copy of this linked data-block, and most of its dependencies " + "when it is a Collection or and Object"); + RNA_def_function_flag(func, FUNC_USE_MAIN); + parm = RNA_def_pointer(func, "id", "ID", "", "New overridden local copy of the root ID"); + RNA_def_function_return(func, parm); + parm = RNA_def_pointer( + func, "scene", "Scene", "", "In which scene the new overrides should be instantiated"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "view_layer", + "ViewLayer", + "", + "In which view layer the new overrides should be instantiated"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_pointer(func, + "reference", + "ID", + "", + "Another ID (usually an Object or Collection) used to decide where to " + "instantiate the new overrides"); + func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create"); RNA_def_function_ui_description(func, "Create an override template for this ID"); RNA_def_function_flag(func, FUNC_USE_REPORTS); @@ -1917,6 +1961,13 @@ static void rna_def_ID(BlenderRNA *brna) "e.g. when calling :class:`bpy.types.Scene.update`"); RNA_def_enum_flag(func, "refresh", update_flag_items, 0, "", "Type of updates to perform"); + func = RNA_def_function(srna, "preview_ensure", "BKE_previewimg_id_ensure"); + RNA_def_function_ui_description(func, + "Ensure that this ID has preview data (if ID type supports it)"); + parm = RNA_def_pointer( + func, "preview_image", "ImagePreview", "", "The existing or created preview"); + RNA_def_function_return(func, parm); + # ifdef WITH_PYTHON RNA_def_struct_register_funcs(srna, NULL, NULL, "rna_ID_instance"); # endif diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 150a455f1c7..948fef1b51e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6115,13 +6115,25 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id) path = ""; } - char id_esc[(sizeof(id->name) - 2) * 2]; + char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4]; + if (id->lib != NULL) { + int ofs = 0; + memcpy(lib_filepath_esc, ", \"", 3); + ofs += 3; + ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc)); + memcpy(lib_filepath_esc + ofs, "\"", 2); + } + else { + lib_filepath_esc[0] = '\0'; + } + char id_esc[(sizeof(id->name) - 2) * 2]; BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc)); - return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s", + return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s", BKE_idtype_idcode_to_name_plural(GS(id->name)), id_esc, + lib_filepath_esc, path[0] ? "." : "", path); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 206ebc2cb14..54f9a93d90a 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -650,7 +650,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, seq->strip->proxy->anim = NULL; } - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + SEQ_relations_invalidate_cache_raw(scene, seq); } else { SEQ_ALL_BEGIN (scene->ed, seq) { diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index a3934b12a44..b363dcd4ba9 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -2598,6 +2598,12 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Maximum Z", "Highest Z value to allow"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "euler_order", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "euler_order"); + RNA_def_property_enum_items(prop, euler_order_items); + RNA_def_property_ui_text(prop, "Euler Order", "Explicitly specify the euler rotation order"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index abc96ddc820..e5e7564f578 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1376,7 +1376,7 @@ static void rna_def_charinfo(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); // RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this character"); RNA_def_property_int_funcs(prop, "rna_ChariInfo_material_index_get", "rna_ChariInfo_material_index_set", @@ -2068,7 +2068,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this curve"); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Curve_material_index_range"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 91e13a4bee3..19ed5f960cf 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1625,7 +1625,7 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) /* Material Index */ prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke"); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this stroke"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Settings */ @@ -2114,6 +2114,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) "ViewLayer", "Only include Layer in this View Layer render output (leave blank to include always)"); + prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); + RNA_def_property_ui_text( + prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* blend mode */ prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "blend_mode"); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index f772f9b5573..d5a1047d287 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1823,7 +1823,7 @@ static void rna_def_mlooptri(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_int_funcs(prop, "rna_MeshLoopTriangle_material_index_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this triangle"); prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1933,7 +1933,7 @@ static void rna_def_mpolygon(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this polygon"); # if 0 RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range"); # endif diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 11f5ff0441a..2bafbd57e11 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -127,6 +127,20 @@ static const EnumPropertyItem node_chunksize_items[] = { }; #endif +static const EnumPropertyItem rna_enum_execution_mode_items[] = { + {NTREE_EXECUTION_MODE_TILED, + "TILED", + 0, + "Tiled", + "Compositing is tiled, having as priority to display first tiles as fast as possible"}, + {NTREE_EXECUTION_MODE_FULL_FRAME, + "FULL_FRAME", + 0, + "Full Frame", + "Composites full image result as fast as possible"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_mapping_type_items[] = { {NODE_MAPPING_TYPE_POINT, "POINT", 0, "Point", "Transform a point"}, {NODE_MAPPING_TYPE_TEXTURE, @@ -234,6 +248,7 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = { {NODE_VECTOR_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"}, {NODE_VECTOR_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "Entry-wise multiply"}, {NODE_VECTOR_MATH_DIVIDE, "DIVIDE", 0, "Divide", "Entry-wise divide"}, + {NODE_VECTOR_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"}, {0, "", ICON_NONE, NULL, NULL}, {NODE_VECTOR_MATH_CROSS_PRODUCT, "CROSS_PRODUCT", 0, "Cross Product", "A cross B"}, {NODE_VECTOR_MATH_PROJECT, "PROJECT", 0, "Project", "Project A onto B"}, @@ -3973,7 +3988,7 @@ static void rna_NodeCryptomatte_layer_name_set(PointerRNA *ptr, int new_value) } } -static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UNUSED(C), +static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) @@ -3984,7 +3999,7 @@ static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UN EnumPropertyItem template = {0, "", 0, "", ""}; int totitem = 0; - ntreeCompositCryptomatteUpdateLayerNames(node); + ntreeCompositCryptomatteUpdateLayerNames(CTX_data_scene(C), node); int layer_index; LISTBASE_FOREACH_INDEX (CryptomatteLayer *, layer, &storage->runtime.layers, layer_index) { template.value = layer_index; @@ -4076,7 +4091,7 @@ static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value) static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr) { - ntreeCompositCryptomatteSyncFromAdd(ptr->data); + ntreeCompositCryptomatteSyncFromAdd(scene, ptr->data); rna_Node_update(bmain, scene, ptr); } @@ -9236,12 +9251,12 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type A", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type B", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } @@ -11671,6 +11686,12 @@ static void rna_def_composite_nodetree(BlenderRNA *brna) RNA_def_struct_sdna(srna, "bNodeTree"); RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + prop = RNA_def_property(srna, "execution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "execution_mode"); + RNA_def_property_enum_items(prop, rna_enum_execution_mode_items); + RNA_def_property_ui_text(prop, "Execution Mode", "Set how compositing is executed"); + RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); + prop = RNA_def_property(srna, "render_quality", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "render_quality"); RNA_def_property_enum_items(prop, node_quality_items); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9ba92431723..41b60bed5bb 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1346,6 +1346,11 @@ static void rna_def_strip_element(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "orig_height"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Orig Height", "Original image height"); + + prop = RNA_def_property(srna, "orig_fps", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "orig_fps"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second"); } static void rna_def_strip_crop(BlenderRNA *brna) @@ -1513,7 +1518,7 @@ static void rna_def_strip_proxy(BlenderRNA *brna) prop = RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "quality"); - RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build"); + RNA_def_property_ui_text(prop, "Quality", "Quality of proxies to build"); RNA_def_property_ui_range(prop, 1, 100, 1, -1); prop = RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE); @@ -2412,12 +2417,6 @@ static void rna_def_movie(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Movie Sequence", "Sequence strip to load a video"); RNA_def_struct_sdna(srna, "Sequence"); - prop = RNA_def_property(srna, "mpeg_preseek", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "anim_preseek"); - RNA_def_property_range(prop, 0, 50); - RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames"); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); - prop = RNA_def_property(srna, "stream_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "streamindex"); RNA_def_property_range(prop, 0, 20); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 161db81332d..1209f22dfd3 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4199,6 +4199,14 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop, "Fade Inactive Objects", "Fade inactive geometry using the viewport background color"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_mode_transfer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_MODE_TRANSFER); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, + "Flash on Mode Transfer", + "Flash the target object when tranfering the active mode to it"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "fade_inactive_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.fade_alpha"); RNA_def_property_ui_text(prop, "Opacity", "Strength of the fade effect"); @@ -4562,7 +4570,6 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "gpencil_vertex_paint_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_vertex_paint_opacity"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Vertex Paint mix factor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); } @@ -5538,7 +5545,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXIES); RNA_def_property_ui_text( prop, "Use Proxies", "Use optimized files for faster scrubbing when available"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 97852123954..4ec6bc68f72 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -620,7 +620,7 @@ static void uilist_filter_items(uiList *ui_list, items_shown = flt_data->items_shown = shown_idx; flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * items_shown, __func__); /* And now, bring back new indices into the [0, items_shown[ range! - * XXX This is O(N²)... :/ + * XXX This is O(N^2). :/ */ for (shown_idx = 0, prev_ni = -1; shown_idx < items_shown; shown_idx++) { for (i = 0, t_ni = len, t_idx = -1; i < items_shown; i++) { diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index bacd3943141..6005ec96255 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6276,6 +6276,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "New Point Cloud Type", "Enable the new point cloud type in the ui"); + prop = RNA_def_property(srna, "use_full_frame_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_full_frame_compositor", 1); + RNA_def_property_ui_text(prop, + "Full Frame Compositor", + "Enable compositor full frame execution mode option (no tiling, " + "reduces execution time and memory usage)"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1); RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 91327b97fe4..0138dd0c3ad 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -195,7 +195,11 @@ endif() if(WITH_TBB) 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() list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} ) diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 9b8782737c3..4b9b24e4e47 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -283,11 +283,10 @@ static void BMD_mesh_intersection(BMesh *bm, /* main bmesh intersection setup */ /* create tessface & intersect */ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3] = (BMLoop * (*)[3]) MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); /* postpone this until after tessellating * so we can use the original normals before the vertex are moved */ @@ -364,7 +363,7 @@ static void BMD_mesh_intersection(BMesh *bm, BM_mesh_intersect(bm, looptris, - tottri, + looptris_tot, bm_face_isect_pair, nullptr, false, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 812bfe3b375..a77f6cfe8ba 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -69,6 +69,19 @@ using blender::MutableSpan; using blender::Span; using blender::Vector; +/* For delete geometry node. */ +void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map); +void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map); +void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map, + Span<int> masked_poly_indices, + Span<int> new_loop_starts); + static void initData(ModifierData *md) { MaskModifierData *mmd = (MaskModifierData *)md; @@ -237,9 +250,7 @@ static void computed_masked_polygons(const Mesh *mesh, *r_num_masked_loops = num_masked_loops; } -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map) +void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); for (const int i_src : vertex_map.index_range()) { @@ -256,10 +267,10 @@ static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, } } -static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map, - Span<int> edge_map) +void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); BLI_assert(src_mesh.totedge == edge_map.size()); @@ -279,12 +290,12 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, } } -static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map, - Span<int> edge_map, - Span<int> masked_poly_indices, - Span<int> new_loop_starts) +void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map, + Span<int> masked_poly_indices, + Span<int> new_loop_starts) { for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 37a21a3c14b..8fa80025790 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -36,6 +36,7 @@ #include "DNA_collection_types.h" #include "DNA_defaults.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -123,6 +124,18 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids) ids.add(&collection->id); } } + else if (socket->type == SOCK_MATERIAL) { + Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value; + if (material != nullptr) { + ids.add(&material->id); + } + } + else if (socket->type == SOCK_TEXTURE) { + Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value; + if (texture != nullptr) { + ids.add(&texture->id); + } + } } } @@ -179,7 +192,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec if (object.type == OB_EMPTY && object.instance_collection != nullptr) { add_collection_relation(ctx, *object.instance_collection); } - else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE)) { + else if (DEG_object_has_geometry_component(&object)) { DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier"); DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask); } @@ -197,18 +210,28 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte find_used_ids_from_settings(nmd->settings, used_ids); find_used_ids_from_nodes(*nmd->node_group, used_ids); for (ID *id : used_ids) { - if (GS(id->name) == ID_OB) { - Object *object = reinterpret_cast<Object *>(id); - add_object_relation(ctx, *object); - } - if (GS(id->name) == ID_GR) { - Collection *collection = reinterpret_cast<Collection *>(id); - add_collection_relation(ctx, *collection); + switch ((ID_Type)GS(id->name)) { + case ID_OB: { + Object *object = reinterpret_cast<Object *>(id); + add_object_relation(ctx, *object); + break; + } + case ID_GR: { + Collection *collection = reinterpret_cast<Collection *>(id); + add_collection_relation(ctx, *collection); + break; + } + case ID_TE: { + DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier"); + } + default: { + /* Purposefully don't add relations for materials. While there are material sockets, + * the pointers are only passed around as handles rather than dereferenced. */ + break; + } } } } - - /* TODO: Add dependency for adding and removing objects in collections. */ } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) @@ -560,6 +583,48 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso }; return &collection_type; } + case SOCK_TEXTURE: { + static const SocketPropertyType collection_type = { + [](const bNodeSocket &socket, const char *name) { + bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, name); + }, + nullptr, + nullptr, + nullptr, + nullptr, + [](const IDProperty &property) { return property.type == IDP_ID; }, + [](const IDProperty &property, void *r_value) { + ID *id = IDP_Id(&property); + Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr; + *(Tex **)r_value = texture; + }, + }; + return &collection_type; + } + case SOCK_MATERIAL: { + static const SocketPropertyType collection_type = { + [](const bNodeSocket &socket, const char *name) { + bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, name); + }, + nullptr, + nullptr, + nullptr, + nullptr, + [](const IDProperty &property) { return property.type == IDP_ID; }, + [](const IDProperty &property, void *r_value) { + ID *id = IDP_Id(&property); + Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr; + *(Material **)r_value = material; + }, + }; + return &collection_type; + } default: { return nullptr; } @@ -1087,6 +1152,14 @@ static void draw_property_for_socket(uiLayout *layout, ICON_OUTLINER_COLLECTION); break; } + case SOCK_MATERIAL: { + uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "materials", socket.name, ICON_MATERIAL); + break; + } + case SOCK_TEXTURE: { + uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE); + break; + } default: uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); } diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index ac5249b40a2..10ef2f4d8eb 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -175,7 +175,7 @@ enum class NodeScheduleState { struct NodeState { /** - * Needs to be locked when any data in this state is accessed that is not explicitely marked as + * Needs to be locked when any data in this state is accessed that is not explicitly marked as * otherwise. */ std::mutex mutex; @@ -192,12 +192,12 @@ struct NodeState { MutableSpan<OutputState> outputs; /** - * Nodes that don't support lazyness have some special handling the first time they are executed. + * Nodes that don't support laziness have some special handling the first time they are executed. */ bool non_lazy_node_is_initialized = false; /** - * Used to check that nodes that don't support lazyness do not run more than once. + * Used to check that nodes that don't support laziness do not run more than once. */ bool has_been_executed = false; @@ -309,9 +309,9 @@ static const CPPType *get_socket_cpp_type(const SocketRef &socket) return nodes::socket_cpp_type_get(*socket.typeinfo()); } -static bool node_supports_lazyness(const DNode node) +static bool node_supports_laziness(const DNode node) { - return node->typeinfo()->geometry_node_execute_supports_lazyness; + return node->typeinfo()->geometry_node_execute_supports_laziness; } /** Implements the callbacks that might be called when a node is executed. */ @@ -380,7 +380,8 @@ class GeometryNodesEvaluator { void execute() { - task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH); + /* Disable threading until T88598 is resolved. */ + task_pool_ = BLI_task_pool_create_no_threads(this); this->create_states_for_reachable_nodes(); this->forward_group_inputs(); @@ -644,10 +645,10 @@ class GeometryNodesEvaluator { if (!this->prepare_node_outputs_for_execution(locked_node)) { return false; } - /* Initialize nodes that don't support lazyness. This is done after at least one output is + /* Initialize nodes that don't support laziness. This is done after at least one output is * required and before we check that all required inputs are provided. This reduces the * number of "round-trips" through the task pool by one for most nodes. */ - if (!node_state.non_lazy_node_is_initialized && !node_supports_lazyness(node)) { + if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) { this->initialize_non_lazy_node(locked_node); node_state.non_lazy_node_is_initialized = true; } @@ -722,7 +723,7 @@ class GeometryNodesEvaluator { /* Ignore unavailable/non-data sockets. */ continue; } - /* Nodes that don't support lazyness require all inputs. */ + /* Nodes that don't support laziness require all inputs. */ const DInputSocket input_socket = locked_node.node.input(i); this->set_input_required(locked_node, input_socket); } @@ -789,8 +790,8 @@ class GeometryNodesEvaluator { const bNode &bnode = *node->bnode(); if (node_state.has_been_executed) { - if (!node_supports_lazyness(node)) { - /* Nodes that don't support lazyness must not be executed more than once. */ + if (!node_supports_laziness(node)) { + /* Nodes that don't support laziness must not be executed more than once. */ BLI_assert_unreachable(); } } @@ -923,12 +924,12 @@ class GeometryNodesEvaluator { return; } - const bool supports_lazyness = node_supports_lazyness(locked_node.node); + const bool supports_laziness = node_supports_laziness(locked_node.node); /* Iterating over sockets instead of the states directly, because that makes it easier to * figure out which socket is missing when one of the asserts is hit. */ for (const OutputSocketRef *socket_ref : locked_node.node->outputs()) { OutputState &output_state = locked_node.node_state.outputs[socket_ref->index()]; - if (supports_lazyness) { + if (supports_laziness) { /* Expected that at least all required sockets have been computed. If more outputs become * required later, the node will be executed again. */ if (output_state.output_usage_for_execution == ValueUsage::Required) { @@ -1025,7 +1026,7 @@ class GeometryNodesEvaluator { /* Get all origin sockets, because we have to tag those as required as well. */ Vector<DSocket> origin_sockets; input_socket.foreach_origin_socket( - [&, this](const DSocket origin_socket) { origin_sockets.append(origin_socket); }); + [&](const DSocket origin_socket) { origin_sockets.append(origin_socket); }); if (origin_sockets.is_empty()) { /* If there are no origin sockets, just load the value from the socket directly. */ @@ -1078,7 +1079,7 @@ class GeometryNodesEvaluator { } /* Notify origin nodes that might want to set its inputs as unused as well. */ - socket.foreach_origin_socket([&, this](const DSocket origin_socket) { + socket.foreach_origin_socket([&](const DSocket origin_socket) { if (origin_socket->is_input()) { /* Values from these sockets are loaded directly from the sockets, so there is no node to * notify. */ @@ -1511,7 +1512,7 @@ void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) bool NodeParamsProvider::lazy_require_input(StringRef identifier) { - BLI_assert(node_supports_lazyness(this->dnode)); + BLI_assert(node_supports_laziness(this->dnode)); const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); BLI_assert(socket); @@ -1547,7 +1548,7 @@ bool NodeParamsProvider::output_is_required(StringRef identifier) const bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const { - BLI_assert(node_supports_lazyness(this->dnode)); + BLI_assert(node_supports_laziness(this->dnode)); const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier); BLI_assert(socket); diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index e2d18cf1790..58d70ef3a4a 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -90,6 +90,46 @@ #include "bmesh.h" +/* -------------------------------------------------------------------- */ +/** \name Generic BMesh Utilities + * \{ */ + +static void vert_face_normal_mark_set(BMVert *v) +{ + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + f->no[0] = FLT_MAX; + } +} + +static void vert_face_normal_mark_update(BMVert *v) +{ + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + if (f->no[0] == FLT_MAX) { + BM_face_normal_update(f); + } + } +} + +/** + * Recalculate the normals of all faces connected to `verts`. + */ +static void vert_array_face_normal_update(BMVert **verts, int verts_len) +{ + for (int i = 0; i < verts_len; i++) { + vert_face_normal_mark_set(verts[i]); + } + + for (int i = 0; i < verts_len; i++) { + vert_face_normal_mark_update(verts[i]); + } +} + +/** \} */ + typedef struct { float mat[3][3]; /* Vert that edge is pointing away from, no relation to @@ -1352,13 +1392,25 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f split_face = collapse_face_corners(bm, split_face, 4, vert_buf); } - /* Done with dynamic array, split_face must now be a quad */ - BLI_array_free(vert_buf); + /* `split_face` should now be a quad. */ BLI_assert(split_face->len == 4); + + /* Account for the highly unlikely case that it's not a quad. */ if (split_face->len != 4) { + /* Reuse `vert_buf` for updating normals. */ + BLI_array_clear(vert_buf); + BLI_array_grow_items(vert_buf, split_face->len); + + BM_iter_as_array(bm, BM_FACES_OF_VERT, split_face, (void **)vert_buf, split_face->len); + + vert_array_face_normal_update(vert_buf, split_face->len); + BLI_array_free(vert_buf); return; } + /* Done with dynamic array. */ + BLI_array_free(vert_buf); + /* Get split face's verts */ // BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)verts, 4); BM_face_as_array_vert_quad(split_face, verts); @@ -1373,6 +1425,8 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f } BMO_op_exec(bm, &op); BMO_op_finish(bm, &op); + + vert_array_face_normal_update(frame->verts, 4); } /* If the frame has some vertices that are inside the hull (detached) @@ -1731,6 +1785,11 @@ static void skin_smooth_hulls(BMesh *bm, /* Done with original coordinates */ BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey); + + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BM_face_normal_update(f); + } } /* Returns true if all hulls are successfully built, false otherwise */ diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index d2c011a21d3..bfd4cd81803 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -184,13 +184,17 @@ static void deformVerts(ModifierData *md, surmd->cfra = cfra; - surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); + const bool has_poly = surmd->mesh->totpoly > 0; + const bool has_edge = surmd->mesh->totedge > 0; + if (has_poly || has_edge) { + surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); - if (surmd->mesh->totpoly) { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); - } - else { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + if (has_poly) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); + } + else if (has_edge) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + } } } } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 33b56fd0de0..8baa891df5c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -162,8 +162,10 @@ set(SRC geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc + geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_resample.cc + geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_is_viewport.cc @@ -178,6 +180,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc + geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc @@ -219,7 +222,7 @@ set(SRC shader/nodes/node_shader_camera.c shader/nodes/node_shader_clamp.cc shader/nodes/node_shader_common.c - shader/nodes/node_shader_curves.c + shader/nodes/node_shader_curves.cc shader/nodes/node_shader_displacement.c shader/nodes/node_shader_eevee_specular.c shader/nodes/node_shader_emission.c diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d2a702c30a6..3c43b53da5a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -50,8 +50,10 @@ void register_node_type_geo_attribute_remove(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); +void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_resample(void); +void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); void register_node_type_geo_is_viewport(void); @@ -66,6 +68,7 @@ void register_node_type_geo_mesh_primitive_grid(void); void register_node_type_geo_mesh_primitive_ico_sphere(void); void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); +void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 52d7e097f0d..7b176b2f395 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -200,7 +200,7 @@ class GeoNodeExecParams { /** * Returns true when the output has to be computed. - * Nodes that support lazyness could use the #lazy_output_is_required variant to possibly avoid + * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid * some computations. */ bool output_is_required(StringRef identifier) const @@ -212,7 +212,7 @@ class GeoNodeExecParams { * Tell the evaluator that a specific input is required. * This returns true when the input will only be available in the next execution. * False is returned if the input is available already. - * This can only be used when the node supports lazyness. + * This can only be used when the node supports laziness. */ bool lazy_require_input(StringRef identifier) { @@ -222,7 +222,7 @@ class GeoNodeExecParams { /** * Asks the evaluator if a specific output is required right now. If this returns false, the * value might still need to be computed later. - * This can only be used when the node supports lazyness. + * This can only be used when the node supports laziness. */ bool lazy_output_is_required(StringRef identifier) { diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh index 45de1816549..f3eb1c24087 100644 --- a/source/blender/nodes/NOD_math_functions.hh +++ b/source/blender/nodes/NOD_math_functions.hh @@ -347,6 +347,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera }; switch (operation) { + case NODE_VECTOR_MATH_MULTIPLY_ADD: + return dispatch([](float3 a, float3 b, float3 c) { return a * b + c; }); case NODE_VECTOR_MATH_WRAP: return dispatch([](float3 a, float3 b, float3 c) { return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z)); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ce1813fdac3..19b671de49d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -289,8 +289,10 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") +DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") +DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") @@ -305,6 +307,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index d9b36924516..dca6dc59ca2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -40,61 +40,74 @@ /** \name Cryptomatte * \{ */ +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render( + const bNode &node, const bool use_meta_data) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + + Scene *scene = (Scene *)node.id; + if (!scene) { + return session; + } + BLI_assert(GS(scene->id.name) == ID_SCE); + + if (use_meta_data) { + Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; + RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + if (render) { + RE_ReleaseResult(render); + } + } + + if (session == nullptr) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_scene(scene)); + } + return session; +} + +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image( + const Scene &scene, const bNode &node) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + Image *image = (Image *)node.id; + if (!image) { + return session; + } + BLI_assert(GS(image->id.name) == ID_IM); + + NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage); + ImageUser *iuser = &node_cryptomatte->iuser; + BKE_image_user_frame_calc(image, iuser, scene.r.cfra); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); + RenderResult *render_result = image->rr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + BKE_image_release_ibuf(image, ibuf, nullptr); + return session; +} static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node( - const bNode &node, const int frame_number, const bool use_meta_data) + const Scene &scene, const bNode &node, const bool use_meta_data) { blender::bke::cryptomatte::CryptomatteSessionPtr session; if (node.type != CMP_NODE_CRYPTOMATTE) { return session; } - NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage); switch (node.custom1) { case CMP_CRYPTOMATTE_SRC_RENDER: { - Scene *scene = (Scene *)node.id; - if (!scene) { - return session; - } - BLI_assert(GS(scene->id.name) == ID_SCE); - - if (use_meta_data) { - Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; - RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - if (render) { - RE_ReleaseResult(render); - } - } - - if (session == nullptr) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_scene(scene)); - } - - break; + return cryptomatte_init_from_node_render(node, use_meta_data); } case CMP_CRYPTOMATTE_SRC_IMAGE: { - Image *image = (Image *)node.id; - if (!image) { - break; - } - BLI_assert(GS(image->id.name) == ID_IM); - - ImageUser *iuser = &node_cryptomatte->iuser; - BKE_image_user_frame_calc(image, iuser, frame_number); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); - RenderResult *render_result = image->rr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - BKE_image_release_ibuf(image, ibuf, nullptr); - break; + return cryptomatte_init_from_node_image(scene, node); } } return session; @@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode return nullptr; } -static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash) +static void cryptomatte_add(const Scene &scene, + bNode &node, + NodeCryptomatte &node_cryptomatte, + float encoded_hash) { /* Check if entry already exist. */ if (cryptomatte_find(node_cryptomatte, encoded_hash)) { @@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa CryptomatteEntry *entry = static_cast<CryptomatteEntry *>( MEM_callocN(sizeof(CryptomatteEntry), __func__)); entry->encoded_hash = encoded_hash; - /* TODO(jbakker): Get current frame from scene. */ blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - node, 0, true); + scene, node, true); if (session) { BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name)); } @@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = { {-1, ""}, }; -void ntreeCompositCryptomatteSyncFromAdd(bNode *node) +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node) { BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY)); NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage); if (n->runtime.add[0] != 0.0f) { - cryptomatte_add(*node, *n, n->runtime.add[0]); + cryptomatte_add(*scene, *node, *n, n->runtime.add[0]); zero_v3(n->runtime.add); } } @@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node) zero_v3(n->runtime.remove); } } -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage); BLI_freelistN(&n->runtime.layers); blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); if (session) { for (blender::StringRef layer_name : @@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) } } -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len) +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage; blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); std::string first_layer_name; if (session) { @@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size BLI_strncpy(r_prefix, cstr, prefix_len); } -CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node) +CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node) { blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node( - *node, 0, true); + *scene, *node, true); return session_ptr.release(); } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 81092798ca1..79a98c5ebf0 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -62,4 +62,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, Mesh *create_cube_mesh(const float size); +/** + * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. + */ +void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, + GeometryComponent &result_component, + Span<bool> masks, + const bool invert); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc index 21538db5455..71643df1cb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -101,9 +101,12 @@ template<> inline float3 clamp_value(const float3 val, const float3 min, const f return tmp; } -template<> inline Color4f clamp_value(const Color4f val, const Color4f min, const Color4f max) +template<> +inline ColorGeometry4f clamp_value(const ColorGeometry4f val, + const ColorGeometry4f min, + const ColorGeometry4f max) { - Color4f tmp; + ColorGeometry4f tmp; tmp.r = std::min(std::max(val.r, min.r), max.r); tmp.g = std::min(std::max(val.g, min.g), max.g); tmp.b = std::min(std::max(val.b, min.b), max.b); @@ -214,8 +217,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam break; } case CD_PROP_COLOR: { - Color4f min = params.get_input<Color4f>("Min_003"); - Color4f max = params.get_input<Color4f>("Max_003"); + ColorGeometry4f min = params.get_input<ColorGeometry4f>("Min_003"); + ColorGeometry4f max = params.get_input<ColorGeometry4f>("Max_003"); if (operation == NODE_CLAMP_RANGE) { if (min.r > max.r) { std::swap(min.r, max.r); @@ -230,8 +233,9 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); - clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max); + MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); + clamp_attribute<ColorGeometry4f>( + attribute_input->typed<ColorGeometry4f>(), results, min, max); break; } default: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index b13e82e676d..5293dd8c876 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -83,8 +83,8 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon * currently. */ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttribute_Typed<Color4f> attribute_result = - component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain); + OutputAttribute_Typed<ColorGeometry4f> attribute_result = + component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain); if (!attribute_result) { return; } @@ -92,7 +92,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, 0.0f); - MutableSpan<Color4f> results = attribute_result.as_span(); + MutableSpan<ColorGeometry4f> results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index a2ff1668a06..57ac68b4cd4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -131,16 +131,16 @@ static void do_equal_operation_float3(const VArray<float3> &input_a, } } -static void do_equal_operation_color4f(const VArray<Color4f> &input_a, - const VArray<Color4f> &input_b, +static void do_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a, + const VArray<ColorGeometry4f> &input_b, const float threshold, MutableSpan<bool> span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) < threshold_squared; } } @@ -185,16 +185,16 @@ static void do_not_equal_operation_float3(const VArray<float3> &input_a, } } -static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a, - const VArray<Color4f> &input_b, +static void do_not_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a, + const VArray<ColorGeometry4f> &input_b, const float threshold, MutableSpan<bool> span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; } } @@ -287,8 +287,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f( - attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); + do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(), + attribute_b->typed<ColorGeometry4f>(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_equal_operation_bool( @@ -305,8 +307,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f( - attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); + do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(), + attribute_b->typed<ColorGeometry4f>(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_not_equal_operation_bool( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc index 2fc86269797..599c9e58e52 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -165,9 +165,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_COLOR: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - GVArray_Typed<Color4f> attribute_in = component.attribute_get_for_read<Color4f>( - input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); - MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); + GVArray_Typed<ColorGeometry4f> attribute_in = + component.attribute_get_for_read<ColorGeometry4f>( + input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index 60522fd0f72..389abe3b2aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -110,7 +110,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams break; } case CD_PROP_COLOR: { - const Color4f value = params.get_input<Color4f>("Value_002"); + const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002"); attribute->fill(&value); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index e502a183ef5..a6bd6c0ee32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -120,16 +120,16 @@ static void do_mix_operation_float3(const int blend_mode, static void do_mix_operation_color4f(const int blend_mode, const VArray<float> &factors, - const VArray<Color4f> &inputs_a, - const VArray<Color4f> &inputs_b, - VMutableArray<Color4f> &results) + const VArray<ColorGeometry4f> &inputs_a, + const VArray<ColorGeometry4f> &inputs_b, + VMutableArray<ColorGeometry4f> &results) { const int size = results.size(); parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; - Color4f a = inputs_a[i]; - const Color4f b = inputs_b[i]; + ColorGeometry4f a = inputs_a[i]; + const ColorGeometry4f b = inputs_b[i]; ramp_blend(blend_mode, a, factor, b); results.set(i, a); } @@ -160,9 +160,9 @@ static void do_mix_operation(const CustomDataType result_type, else if (result_type == CD_PROP_COLOR) { do_mix_operation_color4f(blend_mode, attribute_factor, - attribute_a.typed<Color4f>(), - attribute_b.typed<Color4f>(), - attribute_result.typed<Color4f>()); + attribute_a.typed<ColorGeometry4f>(), + attribute_b.typed<ColorGeometry4f>(), + attribute_result.typed<ColorGeometry4f>()); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index aa558314b9e..d6b1ad3e9e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -79,8 +79,9 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttribute_Typed<Color4f> attribute_out = - component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain); + OutputAttribute_Typed<ColorGeometry4f> attribute_out = + component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name, + result_domain); if (!attribute_out) { return; } @@ -88,7 +89,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>( mapping_name, result_domain, {0, 0, 0}); - MutableSpan<Color4f> colors = attribute_out.as_span(); + MutableSpan<ColorGeometry4f> colors = attribute_out.as_span(); parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { for (const int i : range) { TexResult texture_result = {0}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 8877af445f9..b04e04d1cb7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -59,8 +59,11 @@ static bool operation_use_input_b(const NodeVectorMathOperation operation) static bool operation_use_input_c(const NodeVectorMathOperation operation) { - return ELEM( - operation, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD); + return ELEM(operation, + NODE_VECTOR_MATH_WRAP, + NODE_VECTOR_MATH_REFRACT, + NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD); } static void geo_node_attribute_vector_math_layout(uiLayout *layout, @@ -137,6 +140,7 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op case NODE_VECTOR_MATH_TANGENT: case NODE_VECTOR_MATH_REFRACT: case NODE_VECTOR_MATH_FACEFORWARD: + case NODE_VECTOR_MATH_MULTIPLY_ADD: return CD_PROP_FLOAT3; case NODE_VECTOR_MATH_DOT_PRODUCT: case NODE_VECTOR_MATH_DISTANCE: @@ -495,6 +499,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, break; case NODE_VECTOR_MATH_WRAP: case NODE_VECTOR_MATH_FACEFORWARD: + case NODE_VECTOR_MATH_MULTIPLY_ADD: do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(), attribute_b->typed<float3>(), attribute_c->typed<float3>(), diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc new file mode 100644 index 00000000000..306085e3b75 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#include "BKE_spline.hh" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_length_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_length_out[] = { + {SOCK_FLOAT, N_("Length")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_curve_length_exec(GeoNodeExecParams params) +{ + GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); + curve_set = bke::geometry_set_realize_instances(curve_set); + if (!curve_set.has_curve()) { + params.set_output("Length", 0.0f); + return; + } + const CurveEval &curve = *curve_set.get_curve_for_read(); + float length = 0.0f; + for (const SplinePtr &spline : curve.splines()) { + length += spline->length(); + } + params.set_output("Length", length); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_length() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec; + nodeRegisterType(&ntype); +} 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 1c42b9341a0..684f7d6c702 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -182,6 +182,8 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, } } + output_curve->attributes.reallocate(output_curve->splines().size()); + return output_curve; } 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 f0effdc71f6..fe1b23bf6d7 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 @@ -21,6 +21,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_spline.hh" @@ -236,6 +237,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr } Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc new file mode 100644 index 00000000000..0e053a6d0e9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -0,0 +1,677 @@ +/* + * 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. + */ + +#include "BLI_array.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::bke::CustomDataAttributes; + +/* Code from the mask modifier in MOD_mask.cc. */ +extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span<int> vertex_map); +extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span<int> vertex_map, + blender::Span<int> edge_map); +extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span<int> vertex_map, + blender::Span<int> edge_map, + blender::Span<int> masked_poly_indices, + blender::Span<int> new_loop_starts); + +static bNodeSocketTemplate geo_node_delete_geometry_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Selection")}, + {SOCK_BOOLEAN, N_("Invert")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_delete_geometry_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask) +{ + for (const int i_out : mask.index_range()) { + r_data[i_out] = data[mask[i_out]]; + } +} + +static void spline_copy_builtin_attributes(const Spline &spline, + Spline &r_spline, + const IndexMask mask) +{ + copy_data(spline.positions(), r_spline.positions(), mask); + copy_data(spline.radii(), r_spline.radii(), mask); + copy_data(spline.tilts(), r_spline.tilts(), mask); + switch (spline.type()) { + case Spline::Type::Poly: + break; + case Spline::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); + copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask); + copy_data(src.handle_types_left(), dst.handle_types_left(), mask); + copy_data(src.handle_types_right(), dst.handle_types_right(), mask); + break; + } + case Spline::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; + } + } +} + +static void copy_dynamic_attributes(const CustomDataAttributes &src, + CustomDataAttributes &dst, + const IndexMask mask) +{ + src.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> src_attribute = src.get_for_read(name); + BLI_assert(src_attribute); + + if (!dst.create(name, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. */ + BLI_assert_unreachable(); + } + + std::optional<GMutableSpan> new_attribute = dst.get_for_write(name); + BLI_assert(new_attribute); + + attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) +{ + SplinePtr new_spline = spline.copy_settings(); + new_spline->resize(mask.size()); + + spline_copy_builtin_attributes(spline, *new_spline, mask); + copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); + + return new_spline; +} + +static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve, + const StringRef name, + const bool invert) +{ + Span<SplinePtr> input_splines = input_curve.splines(); + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + + /* Keep track of which splines were copied to the result to copy spline domain attributes. */ + Vector<int64_t> copied_splines; + + if (input_curve.attributes.get_for_read(name)) { + GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false); + for (const int i : input_splines.index_range()) { + if (selection[i] == invert) { + output_curve->add_spline(input_splines[i]->copy()); + copied_splines.append(i); + } + } + } + else { + /* Reuse index vector for each spline. */ + Vector<int64_t> indices_to_copy; + + for (const int i : input_splines.index_range()) { + const Spline &spline = *input_splines[i]; + GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false); + + indices_to_copy.clear(); + for (const int i_point : IndexRange(spline.size())) { + if (selection[i_point] == invert) { + indices_to_copy.append(i_point); + } + } + + /* Avoid creating an empty spline. */ + if (indices_to_copy.is_empty()) { + continue; + } + + SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); + output_curve->add_spline(std::move(new_spline)); + copied_splines.append(i); + } + } + + if (copied_splines.is_empty()) { + return {}; + } + + output_curve->attributes.reallocate(output_curve->splines().size()); + copy_dynamic_attributes( + input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); + + return output_curve; +} + +static void delete_curve_selection(const CurveComponent &in_component, + CurveComponent &r_component, + const StringRef selection_name, + const bool invert) +{ + std::unique_ptr<CurveEval> r_curve = curve_delete( + *in_component.get_for_read(), selection_name, invert); + if (r_curve) { + r_component.replace(r_curve.release()); + } + else { + r_component.clear(); + } +} + +static void delete_point_cloud_selection(const PointCloudComponent &in_component, + PointCloudComponent &out_component, + const StringRef selection_name, + const bool invert) +{ + const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>( + selection_name, ATTR_DOMAIN_POINT, false); + VArray_Span<bool> selection{selection_attribute}; + + const int total = selection.count(invert); + if (total == 0) { + out_component.clear(); + return; + } + out_component.replace(BKE_pointcloud_new_nomain(total)); + + /* Invert the inversion, because this deletes the selected points instead of keeping them. */ + copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert); +} + +static void compute_selected_vertices_from_vertex_selection(const VArray<bool> &vertex_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + uint *r_num_selected_vertices) +{ + BLI_assert(vertex_selection.size() == r_vertex_map.size()); + + uint num_selected_vertices = 0; + for (const int i : r_vertex_map.index_range()) { + if (vertex_selection[i] != invert) { + r_vertex_map[i] = num_selected_vertices; + num_selected_vertices++; + } + else { + r_vertex_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; +} + +static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, + const VArray<bool> &vertex_selection, + const bool invert, + MutableSpan<int> r_edge_map, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == r_edge_map.size()); + + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + + /* Only add the edge if both vertices will be in the new mesh. */ + if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, + const VArray<bool> &vertex_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totvert == vertex_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_verts_in_selection = true; + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (vertex_selection[loop.v] == invert) { + all_verts_in_selection = false; + break; + } + } + + if (all_verts_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge + * are kept along with the edge. + */ +static void compute_selected_vertices_and_edges_from_edge_selection( + const Mesh &mesh, + const VArray<bool> &edge_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + uint *r_num_selected_vertices, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + uint num_selected_edges = 0; + uint num_selected_vertices = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + if (r_vertex_map[edge.v1] == -1) { + r_vertex_map[edge.v1] = num_selected_vertices; + num_selected_vertices++; + } + if (r_vertex_map[edge.v2] == -1) { + r_vertex_map[edge.v2] = num_selected_vertices; + num_selected_vertices++; + } + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that + * polygon is kept. + */ +static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, + const VArray<bool> &edge_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_edges_in_selection = true; + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (edge_selection[loop.e] == invert) { + all_edges_in_selection = false; + break; + } + } + + if (all_edges_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all + * vertices of that polygon or edge are in the selection. + */ +static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, + const VArray<bool> &vertex_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + compute_selected_vertices_from_vertex_selection( + vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to + * that edge are kept as well. The polygons are kept if all edges are in the selection. + */ +static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, + const VArray<bool> &edge_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_vertex_map.fill(-1); + compute_selected_vertices_and_edges_from_edge_selection(mesh, + edge_selection, + invert, + r_vertex_map, + r_edge_map, + r_num_selected_vertices, + r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, + const VArray<bool> &poly_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_vertex_map.fill(-1); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + uint num_selected_vertices = 0; + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_vertex_map[loop.v] == -1) { + r_vertex_map[loop.v] = num_selected_vertices; + num_selected_vertices++; + } + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +using FillMapsFunction = void (*)(const Mesh &mesh, + const VArray<bool> &selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops); + +/** + * Delete the parts of the mesh that are in the selection. The `fill_maps_function` + * depends on the selection type: vertices, edges or faces. + */ +static Mesh *delete_mesh_selection(const Mesh &mesh_in, + const VArray<bool> &selection, + const bool invert, + FillMapsFunction fill_maps_function) +{ + Array<int> vertex_map(mesh_in.totvert); + uint num_selected_vertices; + + Array<int> edge_map(mesh_in.totedge); + uint num_selected_edges; + + Vector<int> selected_poly_indices; + Vector<int> new_loop_starts; + uint num_selected_polys; + uint num_selected_loops; + + /* Fill all the maps based on the selection. We delete everything + * in the selection instead of keeping it, so we need to invert it. */ + fill_maps_function(mesh_in, + selection, + !invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + + Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in, + num_selected_vertices, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); + BKE_mesh_calc_edges_loose(result); + /* Tag to recalculate normals later. */ + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + + return result; +} + +static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name) +{ + std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name); + if (!selection_attribute) { + /* The node will not do anything in this case, but this function must return something. */ + return ATTR_DOMAIN_POINT; + } + + /* Corners can't be deleted separately, so interpolate corner attributes + * to the face domain. Note that this choice is somewhat arbitrary. */ + if (selection_attribute->domain == ATTR_DOMAIN_CORNER) { + return ATTR_DOMAIN_FACE; + } + + return selection_attribute->domain; +} + +static void delete_mesh_selection(MeshComponent &component, + const Mesh &mesh_in, + const StringRef selection_name, + const bool invert) +{ + /* Figure out the best domain to use. */ + const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); + + /* This already checks if the attribute exists, and displays a warning in that case. */ + GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( + selection_name, selection_domain, false); + + /* Check if there is anything to delete. */ + bool delete_nothing = true; + for (const int i : selection.index_range()) { + if (selection[i] != invert) { + delete_nothing = false; + break; + } + } + if (delete_nothing) { + return; + } + + Mesh *mesh_out; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection); + break; + case ATTR_DOMAIN_EDGE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection); + break; + case ATTR_DOMAIN_FACE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection); + break; + default: + BLI_assert_unreachable(); + break; + } + component.replace_mesh_but_keep_vertex_group_names(mesh_out); +} + +static void geo_node_delete_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + const bool invert = params.extract_input<bool>("Invert"); + const std::string selection_name = params.extract_input<std::string>("Selection"); + if (selection_name.empty()) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + GeometrySet out_set(geometry_set); + if (geometry_set.has<PointCloudComponent>()) { + delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(), + out_set.get_component_for_write<PointCloudComponent>(), + selection_name, + invert); + } + if (geometry_set.has<MeshComponent>()) { + delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(), + *geometry_set.get_mesh_for_read(), + selection_name, + invert); + } + if (geometry_set.has<CurveComponent>()) { + delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(), + out_set.get_component_for_write<CurveComponent>(), + selection_name, + invert); + } + + params.set_output("Geometry", std::move(out_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_delete_geometry() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out); + ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index aace71d1d40..f19d533d0b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -214,6 +215,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) } Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); BLI_assert(BKE_mesh_is_valid(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 2806472286e..4c1521aa6f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -561,6 +562,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) Mesh *mesh = create_cylinder_or_cone_mesh( radius_top, radius_bottom, depth, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 1d31068653e..b34913df843 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "bmesh.h" @@ -64,6 +65,7 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const float size = params.extract_input<float>("Size"); Mesh *mesh = create_cube_mesh(size); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index f443b4387d3..c7b9fb920f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -75,6 +76,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 5a4bab86421..ac2f5a23a4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -167,6 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y); BLI_assert(BKE_mesh_is_valid(mesh)); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 242cc6ed7df..5f7d8150022 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "bmesh.h" @@ -67,6 +68,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 2eeb87695a8..d93c4e39fda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -165,6 +166,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) const int count = params.extract_input<int>("Count"); mesh = create_line_mesh(start, delta, count); } + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index cc93e71a5dd..7d340679269 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -296,6 +297,7 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } 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 new file mode 100644 index 00000000000..0fb7910c904 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,316 @@ +/* + * 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. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::Array; + +static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template<typename T> +static void copy_attribute_to_points(const VArray<T> &source_data, + Span<int> map, + MutableSpan<T> dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span<Vector<int>> point_to_vert_maps) +{ + MutableSpan<SplinePtr> splines = curve.splines(); + Set<std::string> source_attribute_names = mesh_component.attribute_names(); + + /* Copy builtin control point attributes. */ + if (source_attribute_names.contains_as("tilt")) { + const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points<float>( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_names.remove_contained_as("tilt"); + } + if (source_attribute_names.contains_as("radius")) { + const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( + "radius", ATTR_DOMAIN_POINT, 1.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points<float>( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_names.remove_contained_as("radius"); + } + + /* Don't copy other builtin control point attributes. */ + source_attribute_names.remove_as("position"); + + /* Copy dynamic control point attributes. */ + for (const StringRef name : source_attribute_names) { + const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name, + ATTR_DOMAIN_POINT); + /* Some attributes might not exist if they were builtin attribute on domains that don't + * have any elements, i.e. a face attribute on the output of the line primitive node. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + /* Create attribute on the spline points. */ + splines[i]->attributes.create(name, data_type); + std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points<T>( + mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr<CurveEval> curve; + Vector<Vector<int>> point_to_vert_maps; +}; + +static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + Vector<Vector<int>> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array<int> neighbor_count(verts.size(), 0); + for (const std::pair<int, int> &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array<int> neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array<int> used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array<int> neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array<int> unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Vector<int> point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Vector<int> point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params, + const MeshComponent &component) +{ + const Mesh &mesh = *component.get_for_read(); + const std::string selection_name = params.extract_input<std::string>("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector<std::pair<int, int>> selected_edges; + for (const int i : IndexRange(mesh.totedge)) { + if (selection[i]) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + } + + return selected_edges; +} + +static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Curve", GeometrySet()); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *component.get_for_read(); + Span<MVert> verts = Span{mesh.mvert, mesh.totvert}; + Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component); + if (selected_edges.size() == 0) { + params.set_output("Curve", GeometrySet()); + return; + } + + CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); + copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); + + params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_curve() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index d2b024b208c..fc04d1e275f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -52,10 +52,10 @@ static void copy_data_based_on_mask(Span<T> data, } } -static void copy_attributes_based_on_mask(const GeometryComponent &in_component, - GeometryComponent &result_component, - Span<bool> masks, - const bool invert) +void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, + GeometryComponent &result_component, + Span<bool> masks, + const bool invert) { for (const std::string &name : in_component.attribute_names()) { ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name); @@ -118,7 +118,7 @@ static void separate_points_from_component(const GeometryComponent &in_component create_component_points(out_component, total); - copy_attributes_based_on_mask(in_component, out_component, masks, invert); + copy_point_attributes_based_on_mask(in_component, out_component, masks, invert); } static GeometrySet separate_geometry_set(const GeometrySet &set_in, @@ -127,6 +127,10 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in, { GeometrySet set_out; for (const GeometryComponent *component : set_in.get_components_for_read()) { + if (component->type() == GEO_COMPONENT_TYPE_CURVE) { + /* Don't support the curve component for now, even though it has a point domain. */ + continue; + } GeometryComponent &out_component = set_out.get_component_for_write(component->type()); separate_points_from_component(*component, out_component, mask_name, invert); } @@ -167,6 +171,6 @@ void register_node_type_geo_point_separate() geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; - ntype.geometry_node_execute_supports_lazyness = true; + ntype.geometry_node_execute_supports_laziness = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index bb0a20f4561..742fafba9e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -22,24 +22,24 @@ static bNodeSocketTemplate geo_node_switch_in[] = { {SOCK_BOOLEAN, N_("Switch")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_BOOLEAN, N_("A")}, - {SOCK_BOOLEAN, N_("B")}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_STRING, N_("A")}, - {SOCK_STRING, N_("B")}, - {SOCK_GEOMETRY, N_("A")}, - {SOCK_GEOMETRY, N_("B")}, - {SOCK_OBJECT, N_("A")}, - {SOCK_OBJECT, N_("B")}, - {SOCK_COLLECTION, N_("A")}, - {SOCK_COLLECTION, N_("B")}, + {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("False")}, + {SOCK_BOOLEAN, N_("True")}, + {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("False")}, + {SOCK_STRING, N_("True")}, + {SOCK_GEOMETRY, N_("False")}, + {SOCK_GEOMETRY, N_("True")}, + {SOCK_OBJECT, N_("False")}, + {SOCK_OBJECT, N_("True")}, + {SOCK_COLLECTION, N_("False")}, + {SOCK_COLLECTION, N_("True")}, {-1, ""}, }; @@ -64,7 +64,7 @@ static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), Pointe static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) { NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); - data->input_type = SOCK_FLOAT; + data->input_type = SOCK_GEOMETRY; node->storage = data; } @@ -91,8 +91,8 @@ static void output_input(GeoNodeExecParams ¶ms, const StringRef input_suffix, const StringRef output_identifier) { - const std::string name_a = "A" + input_suffix; - const std::string name_b = "B" + input_suffix; + const std::string name_a = "False" + input_suffix; + const std::string name_b = "True" + input_suffix; if (input) { params.set_input_unused(name_a); if (params.lazy_require_input(name_b)) { @@ -134,7 +134,7 @@ static void geo_node_switch_exec(GeoNodeExecParams params) break; } case SOCK_RGBA: { - output_input<Color4f>(params, input, "_004", "Output_004"); + output_input<ColorGeometry4f>(params, input, "_004", "Output_004"); break; } case SOCK_STRING: { @@ -165,13 +165,13 @@ void register_node_type_geo_switch() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); node_type_init(&ntype, geo_node_switch_init); node_type_update(&ntype, blender::nodes::geo_node_switch_update); node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; - ntype.geometry_node_execute_supports_lazyness = true; + ntype.geometry_node_execute_supports_laziness = true; ntype.draw_buttons = geo_node_switch_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 9714a4f8a80..1ad5dbea492 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -20,6 +20,7 @@ #include "BLI_float4x4.hh" +#include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" @@ -72,7 +73,8 @@ void transform_mesh(Mesh *mesh, else { const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); BKE_mesh_transform(mesh, matrix.values, false); - BKE_mesh_calc_normals(mesh); + mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; } } @@ -158,7 +160,6 @@ static void transform_curve(CurveEval &curve, const float3 rotation, const float3 scale) { - if (use_translate(rotation, scale)) { curve.translate(translation); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index c6049787970..403f4906d07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -23,6 +23,7 @@ #include "node_geometry_util.hh" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_volume.h" @@ -134,6 +135,7 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in, if (mesh == nullptr) { return; } + BKE_id_material_eval_ensure_default_slot(&mesh->id); MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); dst_component.replace(mesh); } diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc index fb34144abf6..aa23777b664 100644 --- a/source/blender/nodes/intern/math_functions.cc +++ b/source/blender/nodes/intern/math_functions.cc @@ -209,6 +209,8 @@ const FloatMathOperationInfo *get_float3_math_operation_info(const int operation RETURN_OPERATION_INFO("Refract", "vector_math_refract"); case NODE_VECTOR_MATH_FACEFORWARD: RETURN_OPERATION_INFO("Faceforward", "vector_math_faceforward"); + case NODE_VECTOR_MATH_MULTIPLY_ADD: + RETURN_OPERATION_INFO("Multiply Add", "vector_math_multiply_add"); } #undef RETURN_OPERATION_INFO diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 73a702c753a..188d198e159 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -104,9 +104,10 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { - const Color4f value = this->get_input<Color4f>(found_socket->identifier); + const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); - conversions.convert_to_uninitialized(CPPType::get<Color4f>(), *cpp_type, &value, buffer); + conversions.convert_to_uninitialized( + CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer); return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } BLI_assert(false); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index ce2848b52a0..052896d2f48 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -37,6 +37,7 @@ #include "BKE_node.h" #include "DNA_collection_types.h" +#include "DNA_material_types.h" #include "RNA_access.h" #include "RNA_types.h" @@ -396,6 +397,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) id_us_plus(&toval->value->id); break; } + case SOCK_MATERIAL: { + bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value; + bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } } to->flag |= (from->flag & SOCK_HIDE_VALUE); @@ -637,9 +645,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); }; + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); }; socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; + *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; return socktype; } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 5c0bc0b5ebc..8699736e543 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -297,6 +297,12 @@ void OutputSocketRef::foreach_logical_target( skipped_fn.call_safe(target); for (const InternalLinkRef *internal_link : target_node.internal_links()) { if (&internal_link->from() == &target) { + /* The internal link only forwards the first incoming link. */ + if (target.is_multi_input_socket()) { + if (target.directly_linked_links()[0] != link) { + continue; + } + } const OutputSocketRef &mute_output = internal_link->to(); skipped_fn.call_safe(target); skipped_fn.call_safe(mute_output); diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 63f7b8a9ee8..220e5ea9046 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -66,9 +66,9 @@ static bool float_to_bool(const float &a) { return a > 0.0f; } -static Color4f float_to_color(const float &a) +static ColorGeometry4f float_to_color(const float &a) { - return Color4f(a, a, a, 1.0f); + return ColorGeometry4f(a, a, a, 1.0f); } static float3 float2_to_float3(const float2 &a) @@ -87,9 +87,9 @@ static bool float2_to_bool(const float2 &a) { return !is_zero_v2(a); } -static Color4f float2_to_color(const float2 &a) +static ColorGeometry4f float2_to_color(const float2 &a) { - return Color4f(a.x, a.y, 0.0f, 1.0f); + return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); } static bool float3_to_bool(const float3 &a) @@ -108,9 +108,9 @@ static float2 float3_to_float2(const float3 &a) { return float2(a); } -static Color4f float3_to_color(const float3 &a) +static ColorGeometry4f float3_to_color(const float3 &a) { - return Color4f(a.x, a.y, a.z, 1.0f); + return ColorGeometry4f(a.x, a.y, a.z, 1.0f); } static bool int_to_bool(const int32_t &a) @@ -129,9 +129,9 @@ static float3 int_to_float3(const int32_t &a) { return float3((float)a); } -static Color4f int_to_color(const int32_t &a) +static ColorGeometry4f int_to_color(const int32_t &a) { - return Color4f((float)a, (float)a, (float)a, 1.0f); + return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } static float bool_to_float(const bool &a) @@ -150,28 +150,28 @@ static float3 bool_to_float3(const bool &a) { return (a) ? float3(1.0f) : float3(0.0f); } -static Color4f bool_to_color(const bool &a) +static ColorGeometry4f bool_to_color(const bool &a) { - return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f); + return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); } -static bool color_to_bool(const Color4f &a) +static bool color_to_bool(const ColorGeometry4f &a) { return rgb_to_grayscale(a) > 0.0f; } -static float color_to_float(const Color4f &a) +static float color_to_float(const ColorGeometry4f &a) { return rgb_to_grayscale(a); } -static int32_t color_to_int(const Color4f &a) +static int32_t color_to_int(const ColorGeometry4f &a) { return (int)rgb_to_grayscale(a); } -static float2 color_to_float2(const Color4f &a) +static float2 color_to_float2(const ColorGeometry4f &a) { return float2(a.r, a.g); } -static float3 color_to_float3(const Color4f &a) +static float3 color_to_float3(const ColorGeometry4f &a) { return float3(a.r, a.g, a.b); } @@ -184,37 +184,37 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float, float3, float_to_float3>(conversions); add_implicit_conversion<float, int32_t, float_to_int>(conversions); add_implicit_conversion<float, bool, float_to_bool>(conversions); - add_implicit_conversion<float, Color4f, float_to_color>(conversions); + add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions); add_implicit_conversion<float2, float3, float2_to_float3>(conversions); add_implicit_conversion<float2, float, float2_to_float>(conversions); add_implicit_conversion<float2, int32_t, float2_to_int>(conversions); add_implicit_conversion<float2, bool, float2_to_bool>(conversions); - add_implicit_conversion<float2, Color4f, float2_to_color>(conversions); + add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions); add_implicit_conversion<float3, bool, float3_to_bool>(conversions); add_implicit_conversion<float3, float, float3_to_float>(conversions); add_implicit_conversion<float3, int32_t, float3_to_int>(conversions); add_implicit_conversion<float3, float2, float3_to_float2>(conversions); - add_implicit_conversion<float3, Color4f, float3_to_color>(conversions); + add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions); add_implicit_conversion<int32_t, bool, int_to_bool>(conversions); add_implicit_conversion<int32_t, float, int_to_float>(conversions); add_implicit_conversion<int32_t, float2, int_to_float2>(conversions); add_implicit_conversion<int32_t, float3, int_to_float3>(conversions); - add_implicit_conversion<int32_t, Color4f, int_to_color>(conversions); + add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions); add_implicit_conversion<bool, float, bool_to_float>(conversions); add_implicit_conversion<bool, int32_t, bool_to_int>(conversions); add_implicit_conversion<bool, float2, bool_to_float2>(conversions); add_implicit_conversion<bool, float3, bool_to_float3>(conversions); - add_implicit_conversion<bool, Color4f, bool_to_color>(conversions); + add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions); - add_implicit_conversion<Color4f, bool, color_to_bool>(conversions); - add_implicit_conversion<Color4f, float, color_to_float>(conversions); - add_implicit_conversion<Color4f, int32_t, color_to_int>(conversions); - add_implicit_conversion<Color4f, float2, color_to_float2>(conversions); - add_implicit_conversion<Color4f, float3, color_to_float3>(conversions); + add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions); + add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions); + add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions); + add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions); + add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions); return conversions; } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 42299a193e2..f1d5040a292 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.c +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -47,7 +47,7 @@ static void node_shader_exec_curve_vec(void *UNUSED(data), /* stack order input: vec */ /* stack order output: vec */ nodestack_get_vec(vec, SOCK_VECTOR, in[1]); - BKE_curvemapping_evaluate3F(node->storage, out[0]->vec, vec); + BKE_curvemapping_evaluate3F((CurveMapping *)node->storage, out[0]->vec, vec); } static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node) @@ -64,7 +64,7 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, float *array, layer; int size; - CurveMapping *cumap = node->storage; + CurveMapping *cumap = (CurveMapping *)node->storage; BKE_curvemapping_table_RGBA(cumap, &array, &size); GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); @@ -104,17 +104,65 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, GPU_uniform(ext_xyz[2])); } +class CurveVecFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveVecFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Vec"}; + signature.single_input<float>("Fac"); + signature.single_input<blender::float3>("Vector"); + signature.single_output<blender::float3>("Vector"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); + const blender::VArray<blender::float3> &vec_in = params.readonly_single_input<blender::float3>( + 1, "Vector"); + blender::MutableSpan<blender::float3> vec_out = + params.uninitialized_single_output<blender::float3>(2, "Vector"); + + for (int64_t i : mask) { + BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]); + if (fac[i] != 1.0f) { + interp_v3_v3v3(vec_out[i], vec_in[i], vec_out[i], fac[i]); + } + } + } +}; + +static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + bNode &bnode = builder.bnode(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap); +} + void register_node_type_sh_curve_vec(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out); node_type_init(&ntype, node_shader_init_curve_vec); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec); + node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); + ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network; nodeRegisterType(&ntype); } @@ -145,7 +193,7 @@ static void node_shader_exec_curve_rgb(void *UNUSED(data), /* stack order output: vec */ nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); nodestack_get_vec(vec, SOCK_VECTOR, in[1]); - BKE_curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec); + BKE_curvemapping_evaluateRGBF((CurveMapping *)node->storage, out[0]->vec, vec); if (fac != 1.0f) { interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac); } @@ -166,7 +214,7 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, int size; bool use_opti = true; - CurveMapping *cumap = node->storage; + CurveMapping *cumap = (CurveMapping *)node->storage; BKE_curvemapping_init(cumap); BKE_curvemapping_table_RGBA(cumap, &array, &size); @@ -230,17 +278,65 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, GPU_uniform(ext_rgba[3])); } +class CurveRGBFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveRGBFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve RGB"}; + signature.single_input<float>("Fac"); + signature.single_input<blender::ColorGeometry4f>("Color"); + signature.single_output<blender::ColorGeometry4f>("Color"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); + const blender::VArray<blender::ColorGeometry4f> &col_in = + params.readonly_single_input<blender::ColorGeometry4f>(1, "Color"); + blender::MutableSpan<blender::ColorGeometry4f> col_out = + params.uninitialized_single_output<blender::ColorGeometry4f>(2, "Color"); + + for (int64_t i : mask) { + BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]); + if (fac[i] != 1.0f) { + interp_v3_v3v3(col_out[i], col_in[i], col_out[i], fac[i]); + } + } + } +}; + +static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + bNode &bnode = builder.bnode(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap); +} + void register_node_type_sh_curve_rgb(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out); node_type_init(&ntype, node_shader_init_curve_rgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb); + node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); + ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c index 403b3e6d9d6..7e7e1b703f1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c @@ -43,8 +43,9 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, { GPUNodeLink *outlink; NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage; - /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */ - unsigned int hash = BLI_hash_string(aov->name) & ~1; + /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and + * `EEVEE_renderpasses_aov_hash`. */ + unsigned int hash = BLI_hash_string(aov->name) << 1; GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink); GPU_material_add_output_link_aov(mat, outlink, hash); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 8ca4a6bab5f..a7239154633 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { static blender::fn::MFSignature create_signature() { blender::fn::MFSignatureBuilder signature{"Separate RGB"}; - signature.single_input<blender::Color4f>("Color"); + signature.single_input<blender::ColorGeometry4f>("Color"); signature.single_output<float>("R"); signature.single_output<float>("G"); signature.single_output<float>("B"); @@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - const blender::VArray<blender::Color4f> &colors = - params.readonly_single_input<blender::Color4f>(0, "Color"); + const blender::VArray<blender::ColorGeometry4f> &colors = + params.readonly_single_input<blender::ColorGeometry4f>(0, "Color"); blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R"); blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G"); blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B"); for (int64_t i : mask) { - blender::Color4f color = colors[i]; + blender::ColorGeometry4f color = colors[i]; rs[i] = color.r; gs[i] = color.g; bs[i] = color.b; @@ -155,8 +155,9 @@ static int gpu_shader_combrgb(GPUMaterial *mat, static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::Color4f> fn{ - "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }}; + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{ + "Combine RGB", + [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 90e8161c09f..5b2eb300aac 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction { { blender::fn::MFSignatureBuilder signature{"Color Band"}; signature.single_input<float>("Value"); - signature.single_output<blender::Color4f>("Color"); + signature.single_output<blender::ColorGeometry4f>("Color"); signature.single_output<float>("Alpha"); return signature.build(); } @@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction { blender::fn::MFContext UNUSED(context)) const override { const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - blender::MutableSpan<blender::Color4f> colors = - params.uninitialized_single_output<blender::Color4f>(1, "Color"); + blender::MutableSpan<blender::ColorGeometry4f> colors = + params.uninitialized_single_output<blender::ColorGeometry4f>(1, "Color"); blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha"); for (int64_t i : mask) { - blender::Color4f color; + blender::ColorGeometry4f color; BKE_colorband_evaluate(&color_band_, values[i], color); colors[i] = color; alphas[i] = color.a; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 472f903e1a5..419a11201aa 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -94,6 +94,8 @@ static const char *gpu_shader_get_name(int mode) return "vector_math_refract"; case NODE_VECTOR_MATH_FACEFORWARD: return "vector_math_faceforward"; + case NODE_VECTOR_MATH_MULTIPLY_ADD: + return "vector_math_multiply_add"; } return nullptr; @@ -134,8 +136,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node NODE_VECTOR_MATH_ABSOLUTE, NODE_VECTOR_MATH_FRACTION, NODE_VECTOR_MATH_NORMALIZE)); - nodeSetSocketAvailability( - sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_FACEFORWARD)); + nodeSetSocketAvailability(sockC, + ELEM(node->custom1, + NODE_VECTOR_MATH_WRAP, + NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD)); nodeSetSocketAvailability(sockScale, ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT)); nodeSetSocketAvailability(sockVector, @@ -154,6 +159,10 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node node_sock_label_clear(sockC); node_sock_label_clear(sockScale); switch (node->custom1) { + case NODE_VECTOR_MATH_MULTIPLY_ADD: + node_sock_label(sockB, "Multiplier"); + node_sock_label(sockC, "Addend"); + break; case NODE_VECTOR_MATH_FACEFORWARD: node_sock_label(sockB, "Incident"); node_sock_label(sockC, "Reference"); diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index f1a8d450ea5..598640f8f68 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1385,7 +1385,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) BMesh *bm; int looptris_tot; - int tottri; BMLoop *(*looptris)[3]; PyObject *ret; @@ -1398,10 +1397,10 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot); - BM_mesh_calc_tessellation(bm, looptris, &tottri); + BM_mesh_calc_tessellation(bm, looptris); - ret = PyList_New(tottri); - for (i = 0; i < tottri; i++) { + ret = PyList_New(looptris_tot); + for (i = 0; i < looptris_tot; i++) { PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3)); } diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 927ec11c376..4de6063098b 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -82,7 +82,10 @@ static PyTypeObject BlenderAppType; static PyStructSequence_Field app_info_fields[] = { {"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"}, - {"version_file", "The blend file version, compatible with ``bpy.data.version``"}, + {"version_file", + "The Blender version, as a tuple, last used to save a .blend file, compatible with " + "``bpy.data.version``. This value should be used for handling compatibility changes between " + "Blender versions"}, {"version_string", "The Blender version formatted as a string"}, {"version_cycle", "The release status of this build alpha/beta/rc/release"}, {"version_char", "Deprecated, always an empty string"}, diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index a0b543097e6..bc05c51414f 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -43,6 +43,9 @@ void bpy_app_generic_callback(struct Main *main, static PyTypeObject BlenderAppCbType; +/** + * See `BKE_callbacks.h` #eCbEvent declaration for the policy on naming. + */ static PyStructSequence_Field app_cb_info_fields[] = { {"frame_change_pre", "Called after frame change for playback and rendering, before any data is evaluated for the " diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index c312012b24c..5d38a3692c3 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -2253,7 +2253,7 @@ static PyObject *Matrix_str(MatrixObject *self) for (col = 0; col < self->num_col; col++) { maxsize[col] = 0; for (row = 0; row < self->num_row; row++) { - const int size = BLI_snprintf( + const int size = BLI_snprintf_rlen( dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col)); maxsize[col] = max_ii(maxsize[col], size); } diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 1acbcc006ca..79ed9e68420 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -961,8 +961,6 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb /* Get data for tessellation */ { - int tris_len_dummy; - coords_len = (uint)bm->totvert; tris_len = (uint)poly_to_tri_count(bm->totface, bm->totloop); @@ -971,8 +969,7 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb looptris = MEM_mallocN(sizeof(*looptris) * (size_t)tris_len, __func__); - BM_mesh_calc_tessellation(bm, looptris, &tris_len_dummy); - BLI_assert(tris_len_dummy == (int)tris_len); + BM_mesh_calc_tessellation(bm, looptris); } { diff --git a/source/blender/python/rna_dump.py b/source/blender/python/rna_dump.py index 7d469d20110..bd45a36e8a4 100644 --- a/source/blender/python/rna_dump.py +++ b/source/blender/python/rna_dump.py @@ -62,7 +62,7 @@ def seek(r, txt, recurs): # print(dir(r)) # basic types - if type_r in (float, int, bool, type(None)): + if type_r in {float, int, bool, type(None)}: if PRINT_DATA: print(txt + ' -> ' + str(r)) return diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 4f49b8cb58f..ad0815892f7 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -46,12 +46,11 @@ enum { /* seq_dupli' flags */ #define SEQ_DUPE_UNIQUE_NAME (1 << 0) -#define SEQ_DUPE_CONTEXT (1 << 1) -#define SEQ_DUPE_ANIM (1 << 2) #define SEQ_DUPE_ALL (1 << 3) /* otherwise only selected are copied */ #define SEQ_DUPE_IS_RECURSIVE_CALL (1 << 4) struct SequencerToolSettings *SEQ_tool_settings_init(void); +struct SequencerToolSettings *SEQ_tool_settings_ensure(struct Scene *scene); void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings); eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene); void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method); diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 31549ff3994..67d3a2e5960 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -45,6 +45,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene, void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); int SEQ_time_cmp_time_startdisp(const void *a, const void *b); +bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h index 9d529089ffc..a4dc80d75db 100644 --- a/source/blender/sequencer/SEQ_utils.h +++ b/source/blender/sequencer/SEQ_utils.h @@ -61,6 +61,7 @@ int SEQ_recursive_apply(struct Sequence *seq, int (*apply_fn)(struct Sequence *, void *), void *arg); void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index a0c95c1c197..5ccf2a027b0 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -518,7 +518,7 @@ static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntr static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); const size_t num_items_read = fread(header, sizeof(*header), 1, file); if (num_items_read < 1) { BLI_assert(!"unable to read disk cache header"); @@ -540,7 +540,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); return fwrite(header, sizeof(*header), 1, file); } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index f3cea273fdf..0bad66c3d7a 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -70,6 +70,7 @@ #include "SEQ_proxy.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "SEQ_utils.h" #include "effects.h" @@ -273,15 +274,6 @@ static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibl * Order of applying these conditions is important. */ static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead) { - /* Sound strips are not rendered. */ - if (seq->type == SEQ_TYPE_SOUND_RAM) { - return false; - } - /* Muted strips are not rendered. */ - if ((seq->flag & SEQ_MUTE) != 0) { - return false; - } - bool seq_have_effect_in_stack = false; Sequence *seq_iter; SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) { @@ -318,7 +310,7 @@ static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timelin SeqCollection *collection = SEQ_collection_create(); LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { SEQ_collection_append_strip(seq, collection); } } @@ -340,6 +332,15 @@ static void collection_filter_channel_up_to_incl(SeqCollection *collection, cons static void collection_filter_rendered_strips(SeqCollection *collection) { Sequence *seq; + + /* Remove sound strips and muted strips from collection, because these are not rendered. + * Function must_render_strip() don't have to check for these strips anymore. */ + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { + SEQ_collection_remove_strip(seq, collection); + } + } + SEQ_ITERATOR_FOREACH (seq, collection) { if (must_render_strip(seq, collection)) { continue; @@ -1124,8 +1125,6 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context, ImBuf *ibuf = NULL; IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size); - IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - if (SEQ_can_use_proxy(context, seq, psize)) { /* Try to get a proxy image. * Movie proxies are handled by ImBuf module with exception of `custom file` setting. */ @@ -1237,6 +1236,12 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, } if (*r_is_proxy_image == false) { + if (sanim && sanim->anim) { + short fps_denom; + float fps_num; + IMB_anim_get_fps(sanim->anim, &fps_denom, &fps_num, true); + seq->strip->stripdata->orig_fps = fps_denom / fps_num; + } seq->strip->stripdata->orig_width = ibuf->x; seq->strip->stripdata->orig_height = ibuf->y; } diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 55c14944a23..d0bc41062a1 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -313,6 +313,17 @@ SequencerToolSettings *SEQ_tool_settings_init(void) return tool_settings; } +SequencerToolSettings *SEQ_tool_settings_ensure(Scene *scene) +{ + SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + if (tool_settings == NULL) { + scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); + tool_settings = scene->toolsettings->sequencer_tool_settings; + } + + return tool_settings; +} + void SEQ_tool_settings_free(SequencerToolSettings *tool_settings) { MEM_freeN(tool_settings); @@ -320,13 +331,13 @@ void SEQ_tool_settings_free(SequencerToolSettings *tool_settings) eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene) { - const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); return tool_settings->fit_method; } void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method) { - SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); tool_settings->fit_method = fit_method; } @@ -505,10 +516,6 @@ static Sequence *seq_dupli(const Scene *scene_src, if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) { SEQ_sequence_base_unique_name_recursive(&scene_dst->ed->seqbase, seqn); } - - if (dupe_flag & SEQ_DUPE_ANIM) { - SEQ_dupe_animdata(scene_dst, seq->name + 2, seqn->name + 2); - } } return seqn; @@ -554,30 +561,21 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src, { Sequence *seq; Sequence *seqn = NULL; - Sequence *last_seq = SEQ_select_active_get((Scene *)scene_src); - /* always include meta's strips */ - int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL; for (seq = seqbase->first; seq; seq = seq->next) { seq->tmp = NULL; if ((seq->flag & SELECT) || (dupe_flag & SEQ_DUPE_ALL)) { seqn = seq_dupli(scene_src, scene_dst, nseqbase, seq, dupe_flag, flag); - if (seqn) { /*should never fail */ - if (dupe_flag & SEQ_DUPE_CONTEXT) { - seq->flag &= ~SEQ_ALLSEL; - seqn->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK); - } - if (seq->type == SEQ_TYPE_META) { - SEQ_sequence_base_dupli_recursive( - scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag); - } + if (seqn == NULL) { + continue; /* Should never fail. */ + } - if (dupe_flag & SEQ_DUPE_CONTEXT) { - if (seq == last_seq) { - SEQ_select_active_set(scene_dst, seqn); - } - } + if (seq->type == SEQ_TYPE_META) { + /* Always include meta all strip children. */ + int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL; + SEQ_sequence_base_dupli_recursive( + scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag); } } } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 5ec2269b993..9e5afb45f2a 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -548,15 +548,24 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ + float video_fps = 0.0f; + if (anim_arr[0] != NULL) { - seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); IMB_anim_load_metadata(anim_arr[0]); + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + /* Adjust scene's frame rate settings to match. */ if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true); + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; } /* Set initial scale based on load_data->fit_method. */ @@ -577,6 +586,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem"); strip->stripdata->orig_width = orig_width; strip->stripdata->orig_height = orig_height; + strip->stripdata->orig_fps = video_fps; BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name)); seq_add_set_view_transform(scene, seq, load_data); @@ -691,8 +701,6 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo seq->len = IMB_anim_get_duration( sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); - seq->anim_preseek = IMB_anim_get_preseek(sanim->anim); - seq->len -= seq->anim_startofs; seq->len -= seq->anim_endofs; if (seq->len < 0) { diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index 1215cb78b56..7c5a3f031db 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -259,7 +259,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render) SEQ_prefetch_stop(scene); for (seq = seqbase->first; seq; seq = seq->next) { - if (for_render && CFRA >= seq->startdisp && CFRA <= seq->enddisp) { + if (for_render && SEQ_time_strip_intersects_frame(seq, CFRA)) { continue; } @@ -358,7 +358,7 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene, static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) { for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) { - if (seq->enddisp < timeline_frame || seq->startdisp > timeline_frame) { + if (!SEQ_time_strip_intersects_frame(seq, timeline_frame)) { SEQ_relations_sequence_free_anim(seq); } if (seq->type == SEQ_TYPE_META) { diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 40d7fade308..b8b6f62c7aa 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -409,7 +409,7 @@ static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_ { Sequence *seq; SEQ_ITERATOR_FOREACH (seq, all_strips) { - if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { return true; } } @@ -468,3 +468,17 @@ void seq_time_gap_info_get(const Scene *scene, } } } + +/** + * Test if strip intersects with timeline frame. + * Note: This checks if strip would be rendered at this frame. For rendering it is assumed, that + * timeline frame has width of 1 frame and therefore ends at timeline_frame + 1 + * + * \param seq: Sequence to be checked + * \param timeline_frame: absolute frame position + * \return true if strip intersects with timeline frame. + */ +bool SEQ_time_strip_intersects_frame(const Sequence *seq, const int timeline_frame) +{ + return (seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame); +} diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index cf1d7d66476..9aeb2961751 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -43,6 +43,7 @@ #include "SEQ_relations.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "SEQ_utils.h" #include "IMB_imbuf.h" @@ -406,7 +407,7 @@ const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SEQ_MUTE || seq->startdisp > frame || seq->enddisp <= frame) { + if (seq->flag & SEQ_MUTE || !SEQ_time_strip_intersects_frame(seq, frame)) { continue; } /* Only use strips that generate an image, not ones that combine diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 611a9cba000..2ffa04bd8ae 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, struct GPUMatrixUnproject_Precalc unproj_precalc; GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin); uint *buf_iter = buffer; int hit_found = -1; @@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; float co_3d[3]; co_screen[2] = int_as_float(buf_iter[1]); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { select_bias *= gz->scale_final; diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 3efff20f107..0050c834a56 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -282,15 +282,17 @@ bool WM_event_is_mouse_drag(const wmEvent *event) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (WM_event_is_tablet(event)) { - drag_threshold = U.drag_threshold_tablet; - } - else if (ISMOUSE(event->prevtype)) { + if (ISMOUSE(event->prevtype)) { BLI_assert(event->prevtype != 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. */ - drag_threshold = U.drag_threshold_mouse; + if (WM_event_is_tablet(event)) { + drag_threshold = U.drag_threshold_tablet; + } + else { + drag_threshold = U.drag_threshold_mouse; + } } else { /* Typically keyboard, could be NDOF button or other less common types. */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index f0735c562d1..0bdb2953014 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -344,8 +344,13 @@ void WM_init(bContext *C, int argc, const char **argv) (void)argv; /* unused */ #endif - if (!G.background && !wm_start_with_console) { - GHOST_toggleConsole(3); + if (!G.background) { + if (wm_start_with_console) { + GHOST_toggleConsole(1); + } + else { + GHOST_toggleConsole(3); + } } BKE_material_copybuf_clear(); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index c2893317924..519b3781103 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -119,6 +119,10 @@ if(WITH_GMP) add_definitions(-DWITH_GMP) endif() +if(WITH_OPENCOLORIO) + add_definitions(-DWITH_OCIO) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c @@ -285,6 +289,15 @@ if(WITH_PYTHON_MODULE) else() add_executable(blender ${EXETYPE} ${SRC}) + if(WIN32) + add_executable(blender-launcher WIN32 + blender_launcher_win32.c + ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc + ${CMAKE_BINARY_DIR}/blender.exe.manifest + ) + target_compile_definitions (blender-launcher PRIVATE -D_UNICODE -DUNICODE) + target_link_libraries(blender-launcher Pathcch.lib) + endif() endif() if(WITH_BUILDINFO) @@ -870,13 +883,13 @@ elseif(WIN32) if(WITH_TBB) install( FILES - ${LIBDIR}/tbb/lib/tbb.dll + ${LIBDIR}/tbb/bin/tbb.dll DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( FILES - ${LIBDIR}/tbb/lib/debug/tbb_debug.dll + ${LIBDIR}/tbb/bin/tbb_debug.dll DESTINATION "." CONFIGURATIONS Debug ) @@ -884,15 +897,15 @@ elseif(WIN32) if(WITH_TBB_MALLOC_PROXY) install( FILES - ${LIBDIR}/tbb/lib/tbbmalloc.dll - ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll + ${LIBDIR}/tbb/bin/tbbmalloc.dll + ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( FILES - ${LIBDIR}/tbb/lib/debug/tbbmalloc.dll - ${LIBDIR}/tbb/lib/debug/tbbmalloc_proxy.dll + ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll + ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll DESTINATION "." CONFIGURATIONS Debug ) @@ -1212,7 +1225,7 @@ endif() if(WIN32 AND NOT WITH_PYTHON_MODULE) install( - TARGETS blender + TARGETS blender blender-launcher COMPONENT Blender DESTINATION "." ) diff --git a/source/creator/blender_launcher_win32.c b/source/creator/blender_launcher_win32.c new file mode 100644 index 00000000000..86b0f4f3b97 --- /dev/null +++ b/source/creator/blender_launcher_win32.c @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#include <Windows.h> +#include <strsafe.h> + +#include <PathCch.h> + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) +{ + STARTUPINFO siStartInfo = {0}; + PROCESS_INFORMATION procInfo; + wchar_t path[MAX_PATH]; + + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags = STARTF_USESHOWWINDOW; + + /* Get the path to the currently running executable (blender-launcher.exe) */ + + DWORD nSize = GetModuleFileName(NULL, path, MAX_PATH); + if (!nSize) { + return -1; + } + + /* GetModuleFileName returns the number of characters written, but GetLastError needs to be + * called to see if it ran out of space or not. However where would we be without exceptions + * to the rule: "If the buffer is too small to hold the module name, the function returns nSize. + * The last error code remains ERROR_SUCCESS." - source: MSDN. */ + + if (GetLastError() == ERROR_SUCCESS && nSize == MAX_PATH) { + return -1; + } + + /* Remove the filename (blender-launcher.exe) from path. */ + if (PathCchRemoveFileSpec(path, MAX_PATH) != S_OK) { + return -1; + } + + /* Add blender.exe to path, resulting in the full path to the blender executable. */ + if (PathCchCombine(path, MAX_PATH, path, L"blender.exe") != S_OK) { + return -1; + } + + int required_size_chars = lstrlenW(path) + /* Module name */ + 3 + /* 2 quotes + Space */ + lstrlenW(pCmdLine) + /* Original command line */ + 1; /* Zero terminator */ + size_t required_size_bytes = required_size_chars * sizeof(wchar_t); + wchar_t *buffer = (wchar_t *)malloc(required_size_bytes); + if (!buffer) { + return -1; + } + + if (StringCbPrintfEx(buffer, + required_size_bytes, + NULL, + NULL, + STRSAFE_NULL_ON_FAILURE, + L"\"%s\" %s", + path, + pCmdLine) != S_OK) { + free(buffer); + return -1; + } + + BOOL success = CreateProcess( + path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo); + + if (success) { + /* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer + * needed - MSDN. Closing the handles will NOT terminate the thread/process that we just + * started. */ + CloseHandle(procInfo.hThread); + CloseHandle(procInfo.hProcess); + } + + free(buffer); + return success ? 0 : -1; +} diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 36fdaef507b..8b1ac05f086 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -674,6 +674,9 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n"); printf(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n"); printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); +# ifdef WITH_OCIO + printf(" $OCIO Path to override the OpenColorIO config file.\n"); +# endif # ifdef WIN32 printf(" $TEMP Store temporary files here.\n"); # else diff --git a/source/tools b/source/tools -Subproject f99d29ae3e6ad44d45d79309454c45f8088781a +Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8 diff --git a/tests/python/bl_run_operators_event_simulate.py b/tests/python/bl_run_operators_event_simulate.py new file mode 100644 index 00000000000..251d27fce90 --- /dev/null +++ b/tests/python/bl_run_operators_event_simulate.py @@ -0,0 +1,577 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# 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. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +r""" +Overview +======== + +This is a utility to generate events from the command line, +so reproducible test cases can be written without having to create a custom script each time. + +The key differentiating feature for this utility as is that it's able to control modal operators. + +Possible use cases for this script include: + +- Creating reproducible user interactions for the purpose of benchmarking and profiling. + + Note that cursor-motion actions report the update time between events + which can be helpful when measuring optimizations. + +- As a convenient way to replay interactive actions that reproduce a bug. + +- For writing tests (although some extra functionality may be necessary in this case). + + +Actions +======= + +You will notice most of the functionality is supported using the actions command line argument, +this is a kind of mini-language to drive Blender. + +While the current set of commands is fairly limited more can be added as needed. + +To see a list of actions as well as their arguments run: + + ./blender.bin --python tests/python/bl_run_operators_event_simulate.py -- --help + + +Examples +======== + +Rotate in edit-mode examples: + + ./blender.bin \ + --factory-startup \ + --enable-event-simulate \ + --python tests/python/bl_run_operators_event_simulate.py \ + -- \ + --actions \ + 'area_maximize(ui_type="VIEW_3D")' \ + 'operator("object.mode_set", mode="EDIT")' \ + 'operator("mesh.select_all", action="SELECT")' \ + 'operator("mesh.subdivide", number_cuts=5)' \ + 'operator("transform.rotate")' \ + 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=2)' + +Sculpt stroke: + + ./blender.bin \ + --factory-startup \ + --enable-event-simulate \ + --python tests/python/bl_run_operators_event_simulate.py \ + -- \ + --actions \ + 'area_maximize(ui_type="VIEW_3D")' \ + 'event(type="FIVE", value="TAP", ctrl=True)' \ + 'menu("Visual Geometry to Mesh")' \ + 'menu("Frame Selected")' \ + 'menu("Toggle Sculpt Mode")' \ + 'event(type="WHEELDOWNMOUSE", value="TAP", repeat=2)' \ + 'event(type="LEFTMOUSE", value="PRESS")' \ + 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=5)' \ + 'event(type="LEFTMOUSE", value="RELEASE")' + + +Implementation +============== + +While most of the operations listed above can be executed in Python directly, +either the event loop won't be handled between actions (the case for typical Python script), +or the context for executing the actions is not properly set (the case for timers). + +This utility executes actions as if the user initiated them from a key shortcut. +""" + + +import os +import sys +import argparse +from argparse import ArgumentTypeError + +import bpy + + +# ----------------------------------------------------------------------------- +# Constants + +EVENT_TYPES = tuple(bpy.types.Event.bl_rna.properties["type"].enum_items.keys()) +EVENT_VALUES = tuple(bpy.types.Event.bl_rna.properties["value"].enum_items.keys()) +# `TAP` is just convenience for (`PRESS`, `RELEASE`). +EVENT_VALUES_EXTRA = EVENT_VALUES + ('TAP',) + + +# ----------------------------------------------------------------------------- +# Globals + +# Assign a global since this script is not going to be loading new files (which would free the window). +win = bpy.context.window_manager.windows[0] + + +# ----------------------------------------------------------------------------- +# Utilities + +def find_main_area(ui_type=None): + """ + Find the largest area from the current screen. + """ + area_best = None + size_best = -1 + for area in win.screen.areas: + if ui_type is not None: + if ui_type != area.ui_type: + continue + + size = area.width * area.height + if size > size_best: + size_best = size + area_best = area + return area_best + + +def gen_events_type_text(text): + """ + Generate events to type in `text`. + """ + for ch in text: + kw_extra = {} + # The event type in this case is ignored as only the unicode value is used for text input. + type = 'SPACE' + if ch == '\t': + type = 'TAB' + elif ch == '\n': + type = 'RET' + else: + kw_extra["unicode"] = ch + + yield dict(type=type, value='PRESS', **kw_extra) + kw_extra.pop("unicode", None) + yield dict(type=type, value='RELEASE', **kw_extra) + + +# ----------------------------------------------------------------------------- +# Simulate Events + +def mouse_location_get(): + return ( + run_event_simulate.last_event["x"], + run_event_simulate.last_event["y"], + ) + + +def run_event_simulate(*, event_iter): + """ + Pass events from event_iter into Blender. + """ + last_event = run_event_simulate.last_event + + def event_step(): + win = bpy.context.window_manager.windows[0] + + val = next(event_step.run_events, Ellipsis) + if val is Ellipsis: + bpy.app.use_event_simulate = False + print("Finished simulation") + + sys.exit(0) + return None + + # Run event simulation. + for attr in ("x", "y"): + if attr in val: + last_event[attr] = val[attr] + else: + val[attr] = last_event[attr] + + # Fake event value, since press, release is so common. + if val.get("value") == 'TAP': + del val["value"] + win.event_simulate(**val, value='PRESS') + # Needed if new files are loaded. + # win = bpy.context.window_manager.windows[0] + win.event_simulate(**val, value='RELEASE') + else: + # print("val", val) + win.event_simulate(**val) + return 0.0 + + event_step.run_events = iter(event_iter) + + bpy.app.timers.register(event_step, first_interval=0.0, persistent=True) + + +run_event_simulate.last_event = dict( + x=win.width // 2, + y=win.height // 2, +) + + +# ----------------------------------------------------------------------------- +# Action Implementations + +# Static methods from this class are automatically exposed as actions and included in the help text. +class action_handlers: + + @staticmethod + def area_maximize(*, ui_type=None, only_validate=False): + """ + ui_type: + Select the area type (typically 'VIEW_3D'). + Note that this area type needs to exist in the current screen. + """ + if not ((ui_type is None) or (isinstance(ui_type, str))): + raise ArgumentTypeError("'type' argument %r not None or a string type") + + if only_validate: + return + + area = find_main_area(ui_type=ui_type) + if area is None: + raise ArgumentTypeError("Area with ui_type=%r not found" % ui_type) + + x = area.x + (area.width // 2) + y = area.y + (area.height // 2) + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y) + yield dict(type='SPACE', value='TAP', ctrl=True, alt=True) + + x = win.width // 2 + y = win.height // 2 + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y) + + @staticmethod + def menu(text, *, only_validate=False): + """ + text: Menu item to search for and execute. + """ + if not isinstance(text, str): + raise ArgumentTypeError("'text' argument not a string") + + if only_validate: + return + + yield dict(type='F3', value='TAP') + yield from gen_events_type_text(text) + yield dict(type='RET', value='TAP') + + @staticmethod + def event(*, value, type, ctrl=False, alt=False, shift=False, repeat=1, only_validate=False): + """ + value: The event, typically key, e.g. 'ESC', 'RET', 'SPACE', 'A'. + type: The event type, valid values include: 'PRESS', 'RELEASE', 'TAP'. + ctrl: Control modifier. + alt: Alt modifier. + shift: Shift modifier. + """ + valid_items = EVENT_VALUES_EXTRA + if value not in valid_items: + raise ArgumentTypeError("'value' argument %r not in %r" % (value, valid_items)) + valid_items = EVENT_TYPES + if type not in valid_items: + raise ArgumentTypeError("'type' argument %r not in %r" % (value, valid_items)) + valid_items = range(1, sys.maxsize) + if repeat not in valid_items: + raise ArgumentTypeError("'repeat' argument %r not in %r" % (repeat, valid_items)) + del valid_items + + if only_validate: + return + + for _ in range(repeat): + yield dict(type=type, ctrl=ctrl, alt=alt, shift=shift, value=value) + + @staticmethod + def cursor_motion(*, path, steps, radius=100, repeat=1, only_validate=False): + """ + path: The path type to use in ('CIRCLE'). + steps: The number of events to generate. + radius: The radius in pixels. + repeat: Number of times to repeat the cursor rotation. + """ + + import time + from math import sin, cos, pi + + valid_items = range(1, sys.maxsize) + if steps not in valid_items: + raise ArgumentTypeError("'steps' argument %r not in %r" % (steps, valid_items)) + + valid_items = range(1, sys.maxsize) + if radius not in valid_items: + raise ArgumentTypeError("'radius' argument %r not in %r" % (steps, valid_items)) + + valid_items = ('CIRCLE',) + if path not in valid_items: + raise ArgumentTypeError("'path' argument %r not in %r" % (path, valid_items)) + + valid_items = range(1, sys.maxsize) + if repeat not in valid_items: + raise ArgumentTypeError("'repeat' argument %r not in %r" % (repeat, valid_items)) + del valid_items + + if only_validate: + return + + x_init, y_init = mouse_location_get() + + y_init_ofs = y_init + radius + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init_ofs) + + print("\n" "Times for: %s" % os.path.basename(bpy.data.filepath)) + + t = time.time() + step_total = 0 + + if path == 'CIRCLE': + for _ in range(repeat): + for i in range(1, steps + 1): + phi = (i / steps) * 2.0 * pi + x_ofs = -radius * sin(phi) + y_ofs = +radius * cos(phi) + step_total += 1 + yield dict( + type='MOUSEMOVE', + value='NOTHING', + x=int(x_init + x_ofs), + y=int(y_init + y_ofs), + ) + + delta = time.time() - t + delta_step = delta / step_total + print( + "Average:", + ("%.6f FPS" % (1 / delta_step)).rjust(10), + ) + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init) + + @staticmethod + def operator(idname, *, only_validate=False, **kw): + """ + idname: The operator identifier (positional argument only). + kw: Passed to the operator. + """ + + # Create a temporary key binding to call the operator. + wm = bpy.context.window_manager + keyconf = wm.keyconfigs.user + + keymap_id = "Screen" + key_to_map = 'F24' + + if only_validate: + op_mod, op_submod = idname.partition(".")[0::2] + op = getattr(getattr(bpy.ops, op_mod), op_submod) + try: + # The poll result doesn't matter we only want to know if the operator exists or not. + op.poll() + except AttributeError: + raise ArgumentTypeError("Operator %r does not exist" % (idname)) + + keymap = keyconf.keymaps[keymap_id] + kmi = keymap.keymap_items.new(idname=idname, type=key_to_map, value='PRESS') + kmi.idname = idname + props = kmi.properties + for key, value in kw.items(): + if not hasattr(props, key): + raise ArgumentTypeError("Operator %r does not have a %r property" % (idname, key)) + + try: + setattr(props, key, value) + except Exception as ex: + raise ArgumentTypeError("Operator %r assign %r property with error %s" % (idname, key, str(ex))) + + keymap.keymap_items.remove(kmi) + return + + keymap = keyconf.keymaps[keymap_id] + kmi = keymap.keymap_items.new(idname=idname, type=key_to_map, value='PRESS') + kmi.idname = idname + props = kmi.properties + for key, value in kw.items(): + setattr(props, key, value) + + yield dict(type=key_to_map, value='TAP') + + keymap = keyconf.keymaps[keymap_id] + kmi = keymap.keymap_items[-1] + keymap.keymap_items.remove(kmi) + + +ACTION_DIR = tuple([ + key for key in sorted(action_handlers.__dict__.keys()) + if not key.startswith("_") +]) + + +def handle_action(op, args, kwargs, only_validate=False): + fn = getattr(action_handlers, op, None) + if fn is None: + raise ArgumentTypeError("Action %r is not found in %r" % (op, ACTION_DIR)) + yield from fn(*args, **kwargs, only_validate=only_validate) + + +# ----------------------------------------------------------------------------- +# Argument Parsing + + +class BlenderAction(argparse.Action): + """ + This class is used to extract positional & keyword arguments from + a string, validate them, and return the (action, positional_args, keyword_args). + + All of this happens during argument parsing so any errors in the actions + show useful error messages instead of failing to execute part way through. + """ + + @staticmethod + def _parse_value(value, index): + """ + Convert: + "value(1, 2, a=1, b='', c=None)" + To: + ("value", (1, 2), {"a": 1, "b": "", "c": None}) + """ + split = value.find("(") + if split == -1: + op = value + args = None + kwargs = None + else: + op = value[:split] + namespace = {op: lambda *args, **kwargs: (args, kwargs)} + expr = value + try: + args, kwargs = eval(expr, namespace, namespace) + except Exception as ex: + raise ArgumentTypeError("Unable to parse \"%s\" at index %d, error: %s" % (expr, index, str(ex))) + + # Creating a list is necessary since this is a generator. + try: + dummy_result = list(handle_action(op, args, kwargs, only_validate=True)) + except ArgumentTypeError as ex: + raise ArgumentTypeError("Invalid 'action' arguments \"%s\" at index %d, %s" % (value, index, str(ex))) + # Validation should never yield any events. + assert(not dummy_result) + + return (op, args, kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + setattr( + namespace, + self.dest, [ + self._parse_value(value, index) + for index, value in enumerate(values) + ], + ) + + +def argparse_create(): + import inspect + import textwrap + + # When --help or no args are given, print this help + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + ) + + # Collect doc-strings from static methods in `actions`. + actions_docstring = [] + for action_key in ACTION_DIR: + action = getattr(action_handlers, action_key) + args = str(inspect.signature(action)) + args = "(" + args[1:].removeprefix("*, ") + args = args.replace(", *, ", ", ") # Needed in case the are positional arguments. + args = args.replace(", only_validate=False", "") + + actions_docstring.append("- %s%s\n" % (action_key, args)) + docs = textwrap.dedent((action.__doc__ or "").lstrip("\n").rstrip()) + "\n\n" + + actions_docstring.append(textwrap.indent(docs, " ")) + + parser.add_argument( + "--actions", + dest="actions", + metavar='ACTIONS', type=str, + help=( + "\n" "Arguments must use one of the following prefix:\n" + "\n" + "".join(actions_docstring) + ), + nargs='+', + required=True, + action=BlenderAction, + ) + + return parser + + +# ----------------------------------------------------------------------------- +# Default Startup + + +def setup_default_preferences(prefs): + """ + Set preferences useful for automation. + """ + prefs.view.show_splash = False + prefs.view.smooth_view = 0 + prefs.view.use_save_prompt = False + prefs.view.show_developer_ui = True + prefs.filepaths.use_auto_save_temporary_files = False + + +# ----------------------------------------------------------------------------- +# Main Function + + +def main_event_iter(*, action_list): + """ + Yield all events from action handlers. + """ + area = find_main_area() + + x_init = area.x + (area.width // 2) + y_init = area.y + (area.height // 2) + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init) + + for (op, args, kwargs) in action_list: + yield from handle_action(op, args, kwargs) + + +def main(): + from sys import argv + argv = argv[argv.index("--") + 1:] if "--" in argv else [] + + try: + args = argparse_create().parse_args(argv) + except ArgumentTypeError as ex: + print(ex) + sys.exit(1) + + setup_default_preferences(bpy.context.preferences) + + run_event_simulate(event_iter=main_event_iter(action_list=args.actions)) + + +if __name__ == "__main__": + main() |