WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 381 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
95 changed files with 1186 additions and 838 deletions
Showing only changes of commit c86d6bf244 - Show all commits

View File

@ -279,5 +279,6 @@ StatementMacros:
MacroBlockBegin: "^OSL_CLOSURE_STRUCT_BEGIN$" MacroBlockBegin: "^OSL_CLOSURE_STRUCT_BEGIN$"
MacroBlockEnd: "^OSL_CLOSURE_STRUCT_END$" MacroBlockEnd: "^OSL_CLOSURE_STRUCT_END$"
# Ensure new line at the end of source files. # Ensure single new line at the end of source files.
InsertNewlineAtEOF: True InsertNewlineAtEOF: True
KeepEmptyLinesAtEOF: False

1
.gitignore vendored
View File

@ -56,6 +56,7 @@ waveletNoiseTile.bin
# External repositories. # External repositories.
/scripts/addons/ /scripts/addons/
/scripts/addons_contrib/ /scripts/addons_contrib/
/tests/benchmarks/
# Ignore old submodules directories. # Ignore old submodules directories.
# Eventually need to get rid of those, but for the first time of transition # Eventually need to get rid of those, but for the first time of transition

29
.gitmodules vendored Normal file
View File

@ -0,0 +1,29 @@
[submodule "lib/linux_x64"]
update = none
path = lib/linux_x64
url = https://projects.blender.org/blender/lib-linux_x64.git
branch = main
[submodule "lib/macos_arm64"]
update = none
path = lib/macos_arm64
url = https://projects.blender.org/blender/lib-macos_arm64.git
branch = main
[submodule "lib/macos_x64"]
update = none
path = lib/macos_x64
url = https://projects.blender.org/blender/lib-macos_x64.git
branch = main
[submodule "lib/windows_x64"]
update = none
path = lib/windows_x64
url = https://projects.blender.org/blender/lib-windows_x64.git
branch = main
[submodule "release/datafiles/assets"]
path = release/datafiles/assets
url = https://projects.blender.org/blender/blender-assets.git
branch = main
[submodule "tests/data"]
update = none
path = tests/data
url = https://projects.blender.org/blender/blender-test-data.git
branch = main

View File

@ -2,7 +2,7 @@
# #
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
# This Makefile does an out-of-source CMake build in ../build_`OS`_`CPU` # This Makefile does an out-of-source CMake build in ../build_`OS`
# eg: # eg:
# ../build_linux_i386 # ../build_linux_i386
# This is for users who like to configure & build blender with a single command. # This is for users who like to configure & build blender with a single command.
@ -35,7 +35,7 @@ Other Convenience Targets
* deps: Build library dependencies (intended only for platform maintainers). * deps: Build library dependencies (intended only for platform maintainers).
The existence of locally build dependencies overrides the pre-built dependencies from subversion. The existence of locally build dependencies overrides the pre-built dependencies from subversion.
These must be manually removed from '../lib/' to go back to using the pre-compiled libraries. These must be manually removed from 'lib/' to go back to using the pre-compiled libraries.
Project Files Project Files
Generate project files for development environments. Generate project files for development environments.
@ -165,6 +165,16 @@ OS:=$(shell uname -s)
OS_NCASE:=$(shell uname -s | tr '[A-Z]' '[a-z]') OS_NCASE:=$(shell uname -s | tr '[A-Z]' '[a-z]')
CPU:=$(shell uname -m) CPU:=$(shell uname -m)
# Use our OS and CPU architecture naming conventions.
ifeq ($(CPU),x86_64)
CPU:=x64
endif
ifeq ($(OS_NCASE),darwin)
OS_LIBDIR:=macos
else
OS_LIBDIR:=$(OS_NCASE)
endif
# Source and Build DIR's # Source and Build DIR's
BLENDER_DIR:=$(shell pwd -P) BLENDER_DIR:=$(shell pwd -P)
@ -186,26 +196,13 @@ ifndef DEPS_BUILD_DIR
endif endif
ifndef DEPS_INSTALL_DIR ifndef DEPS_INSTALL_DIR
DEPS_INSTALL_DIR:=$(shell dirname "$(BLENDER_DIR)")/lib/$(OS_NCASE) DEPS_INSTALL_DIR:=$(shell dirname "$(BLENDER_DIR)")/lib/$(OS_LIBDIR)_$(CPU)
# Add processor type to directory name, except for darwin x86_64
# which by convention does not have it.
ifeq ($(OS_NCASE),darwin)
ifneq ($(CPU),x86_64)
DEPS_INSTALL_DIR:=$(DEPS_INSTALL_DIR)_$(CPU)
endif
else
DEPS_INSTALL_DIR:=$(DEPS_INSTALL_DIR)_$(CPU)
endif
endif endif
# Set the LIBDIR, an empty string when not found. # Set the LIBDIR, an empty string when not found.
LIBDIR:=$(wildcard ../lib/${OS_NCASE}_${CPU}) LIBDIR:=$(wildcard $(BLENDER_DIR)/lib/${OS_LIBDIR}_${CPU})
ifeq (, $(LIBDIR)) ifeq (, $(LIBDIR))
LIBDIR:=$(wildcard ../lib/${OS_NCASE}_${CPU}_glibc_228) LIBDIR:=$(wildcard $(BLENDER_DIR)/lib/${OS_LIBDIR})
endif
ifeq (, $(LIBDIR))
LIBDIR:=$(wildcard ../lib/${OS_NCASE})
endif endif
# Find the newest Python version bundled in `LIBDIR`. # Find the newest Python version bundled in `LIBDIR`.

View File

@ -49,18 +49,17 @@ endif()
if(NOT DEFINED LIBDIR) if(NOT DEFINED LIBDIR)
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64") if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/darwin) set(LIBDIR ${CMAKE_SOURCE_DIR}/lib/macos_x64)
else() else()
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/darwin_${CMAKE_OSX_ARCHITECTURES}) set(LIBDIR ${CMAKE_SOURCE_DIR}/lib/macos_${CMAKE_OSX_ARCHITECTURES})
endif()
else()
if(FIRST_RUN)
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
endif() endif()
endif() endif()
if(NOT EXISTS "${LIBDIR}/") if(NOT EXISTS "${LIBDIR}/.git")
message(FATAL_ERROR "Mac OSX requires pre-compiled libs at: '${LIBDIR}'") message(FATAL_ERROR "Mac OSX requires pre-compiled libs at: '${LIBDIR}'")
endif() endif()
if(FIRST_RUN)
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
endif()
# Avoid searching for headers since this would otherwise override our lib # Avoid searching for headers since this would otherwise override our lib
# directory as well as PYTHON_ROOT_DIR. # directory as well as PYTHON_ROOT_DIR.

View File

@ -16,13 +16,13 @@ else()
set(LIBDIR_NATIVE_ABI ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_NAME}) set(LIBDIR_NATIVE_ABI ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_NAME})
# Path to precompiled libraries with known glibc 2.28 ABI. # Path to precompiled libraries with known glibc 2.28 ABI.
set(LIBDIR_GLIBC228_ABI ${CMAKE_SOURCE_DIR}/../lib/linux_x86_64_glibc_228) set(LIBDIR_GLIBC228_ABI ${CMAKE_SOURCE_DIR}/lib/linux_x64)
# Choose the best suitable libraries. # Choose the best suitable libraries.
if(EXISTS ${LIBDIR_NATIVE_ABI}) if(EXISTS ${LIBDIR_NATIVE_ABI})
set(LIBDIR ${LIBDIR_NATIVE_ABI}) set(LIBDIR ${LIBDIR_NATIVE_ABI})
set(WITH_LIBC_MALLOC_HOOK_WORKAROUND TRUE) set(WITH_LIBC_MALLOC_HOOK_WORKAROUND TRUE)
elseif(EXISTS ${LIBDIR_GLIBC228_ABI}) elseif(EXISTS "${LIBDIR_GLIBC228_ABI}/.git")
set(LIBDIR ${LIBDIR_GLIBC228_ABI}) set(LIBDIR ${LIBDIR_GLIBC228_ABI})
if(WITH_MEM_JEMALLOC) if(WITH_MEM_JEMALLOC)
# jemalloc provides malloc hooks. # jemalloc provides malloc hooks.

View File

@ -266,23 +266,23 @@ if(NOT DEFINED LIBDIR)
# Setup 64bit and 64bit windows systems # Setup 64bit and 64bit windows systems
if(CMAKE_CL_64) if(CMAKE_CL_64)
message(STATUS "64 bit compiler detected.") message(STATUS "64 bit compiler detected.")
set(LIBDIR_BASE "win64") set(LIBDIR_BASE "windows_x64")
else() else()
message(FATAL_ERROR "32 bit compiler detected, blender no longer provides pre-build libraries for 32 bit windows, please set the LIBDIR cmake variable to your own library folder") message(FATAL_ERROR "32 bit compiler detected, blender no longer provides pre-build libraries for 32 bit windows, please set the LIBDIR cmake variable to your own library folder")
endif() endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.30.30423) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.30.30423)
message(STATUS "Visual Studio 2022 detected.") message(STATUS "Visual Studio 2022 detected.")
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) set(LIBDIR ${CMAKE_SOURCE_DIR}/lib/${LIBDIR_BASE})
elseif(MSVC_VERSION GREATER 1919) elseif(MSVC_VERSION GREATER 1919)
message(STATUS "Visual Studio 2019 detected.") message(STATUS "Visual Studio 2019 detected.")
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) set(LIBDIR ${CMAKE_SOURCE_DIR}/lib/${LIBDIR_BASE})
endif() endif()
else() else()
if(FIRST_RUN) if(FIRST_RUN)
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}") message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
endif() endif()
endif() endif()
if(NOT EXISTS "${LIBDIR}/") if(NOT EXISTS "${LIBDIR}/.git")
message(FATAL_ERROR "\n\nWindows requires pre-compiled libs at: '${LIBDIR}'. Please run `make update` in the blender source folder to obtain them.") message(FATAL_ERROR "\n\nWindows requires pre-compiled libs at: '${LIBDIR}'. Please run `make update` in the blender source folder to obtain them.")
endif() endif()

View File

@ -163,14 +163,14 @@ function(blender_add_ctests)
TEST_PREFIX ${ARGS_SUITE_NAME} TEST_PREFIX ${ARGS_SUITE_NAME}
WORKING_DIRECTORY "${TEST_INSTALL_DIR}" WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
EXTRA_ARGS EXTRA_ARGS
--test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests" --test-assets-dir "${CMAKE_SOURCE_DIR}/tests/data"
--test-release-dir "${_test_release_dir}" --test-release-dir "${_test_release_dir}"
) )
else() else()
add_test( add_test(
NAME ${ARGS_SUITE_NAME} NAME ${ARGS_SUITE_NAME}
COMMAND ${ARGS_TARGET} COMMAND ${ARGS_TARGET}
--test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests" --test-assets-dir "${CMAKE_SOURCE_DIR}/tests/data"
--test-release-dir "${_test_release_dir}" --test-release-dir "${_test_release_dir}"
WORKING_DIRECTORY ${TEST_INSTALL_DIR} WORKING_DIRECTORY ${TEST_INSTALL_DIR}
) )

View File

@ -13,6 +13,7 @@ import sys
import make_utils import make_utils
from make_utils import call from make_utils import call
from pathlib import Path
# Parse arguments. # Parse arguments.
@ -21,7 +22,6 @@ def parse_arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--ctest-command", default="ctest") parser.add_argument("--ctest-command", default="ctest")
parser.add_argument("--cmake-command", default="cmake") parser.add_argument("--cmake-command", default="cmake")
parser.add_argument("--svn-command", default="svn")
parser.add_argument("--git-command", default="git") parser.add_argument("--git-command", default="git")
parser.add_argument("--config", default="") parser.add_argument("--config", default="")
parser.add_argument("build_directory") parser.add_argument("build_directory")
@ -30,7 +30,6 @@ def parse_arguments() -> argparse.Namespace:
args = parse_arguments() args = parse_arguments()
git_command = args.git_command git_command = args.git_command
svn_command = args.svn_command
ctest_command = args.ctest_command ctest_command = args.ctest_command
cmake_command = args.cmake_command cmake_command = args.cmake_command
config = args.config config = args.config
@ -45,24 +44,18 @@ if make_utils.command_missing(git_command):
sys.exit(1) sys.exit(1)
# Test if we are building a specific release version. # Test if we are building a specific release version.
branch = make_utils.git_branch(git_command) lib_tests_dirpath = Path("tests") / "data"
tag = make_utils.git_tag(git_command)
release_version = make_utils.git_branch_release_version(branch, tag)
lib_tests_dirpath = os.path.join('..', 'lib', "tests")
if not os.path.exists(lib_tests_dirpath): if not (lib_tests_dirpath / ".git").exists():
print("Tests files not found, downloading...") print("Tests files not found, downloading...")
if make_utils.command_missing(svn_command):
sys.stderr.write("svn not found, can't checkout test files\n")
sys.exit(1)
if make_utils.command_missing(cmake_command): if make_utils.command_missing(cmake_command):
sys.stderr.write("cmake not found, can't checkout test files\n") sys.stderr.write("cmake not found, can't checkout test files\n")
sys.exit(1) sys.exit(1)
svn_url = make_utils.svn_libraries_base_url(release_version) + "/tests" # Ensure the test data files sub-module is configured and present.
call([svn_command, "checkout", svn_url, lib_tests_dirpath]) make_utils.git_enable_submodule(git_command, "tests/data")
make_utils.git_update_submodule(args.git_command, lib_tests_dirpath)
# Run cmake again to detect tests files. # Run cmake again to detect tests files.
os.chdir(build_dir) os.chdir(build_dir)

View File

@ -4,11 +4,11 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
""" """
"make update" for all platforms, updating svn libraries and tests and Blender "make update" for all platforms, updating Git LFS submodules for libraries and
git repository and sub-modules. tests, and Blender git repository.
For release branches, this will check out the appropriate branches of For release branches, this will check out the appropriate branches of
sub-modules and libraries. submodules and libraries.
""" """
import argparse import argparse
@ -20,168 +20,169 @@ import sys
import make_utils import make_utils
from pathlib import Path from pathlib import Path
from make_utils import call, check_output from make_utils import call, check_output
from urllib.parse import urljoin from urllib.parse import urljoin, urlsplit
from typing import ( from typing import Optional
Optional,
)
class Submodule:
path: str
branch: str
branch_fallback: str
def __init__(self, path: str, branch: str, branch_fallback: str) -> None:
self.path = path
self.branch = branch
self.branch_fallback = branch_fallback
def print_stage(text: str) -> None: def print_stage(text: str) -> None:
print("") print("")
print(text) print(text)
print("=" * len(text))
print("") print("")
# Parse arguments
def parse_arguments() -> argparse.Namespace: def parse_arguments() -> argparse.Namespace:
"""
Parse command line line arguments.
Returns parsed object from which the command line arguments can be accessed
as properties. The name of the properties matches the command line argument,
but with the leading dashed omitted and all remaining dashes replaced with
underscore.
"""
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--no-libraries", action="store_true") parser.add_argument("--no-libraries", action="store_true")
parser.add_argument("--no-blender", action="store_true") parser.add_argument("--no-blender", action="store_true")
parser.add_argument("--no-submodules", action="store_true") parser.add_argument("--no-submodules", action="store_true")
parser.add_argument("--use-tests", action="store_true") parser.add_argument("--use-tests", action="store_true")
parser.add_argument("--svn-command", default="svn")
parser.add_argument("--svn-branch", default=None)
parser.add_argument("--git-command", default="git") parser.add_argument("--git-command", default="git")
parser.add_argument("--use-linux-libraries", action="store_true") parser.add_argument("--use-linux-libraries", action="store_true")
parser.add_argument("--architecture", type=str, choices=("x86_64", "amd64", "arm64",)) parser.add_argument("--architecture", type=str,
choices=("x86_64", "amd64", "arm64",))
return parser.parse_args() return parser.parse_args()
def get_blender_git_root() -> str: def get_blender_git_root() -> Path:
return check_output([args.git_command, "rev-parse", "--show-toplevel"]) """
Get root directory of the current Git directory.
"""
return Path(
check_output([args.git_command, "rev-parse", "--show-toplevel"]))
# Setup for precompiled libraries and tests from svn.
def get_effective_platform(args: argparse.Namespace) -> str:
"""
Get platform of the host.
The result string is normalized to the name used by Blender releases and
library repository name prefixes: linux, macos, windows.
"""
if sys.platform == "darwin":
platform = "macos"
elif sys.platform == "win32":
platform = "windows"
else:
platform = sys.platform
assert (platform in ("linux", "macos", "windows"))
return platform
def get_effective_architecture(args: argparse.Namespace) -> str: def get_effective_architecture(args: argparse.Namespace) -> str:
"""
Get architecture of the host.
The result string is normalized to the architecture name used by the Blender
releases and library repository name suffixes: x64, arm64.
NOTE: When cross-compiling the architecture is coming from the command line
argument.
"""
architecture = args.architecture architecture = args.architecture
if architecture: if architecture:
assert isinstance(architecture, str) assert isinstance(architecture, str)
return architecture elif "ARM64" in platform.version():
# Check platform.version to detect arm64 with x86_64 python binary.
# Check platform.version to detect arm64 with x86_64 python binary. architecture = "arm64"
if "ARM64" in platform.version():
return "arm64"
return platform.machine().lower()
def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None:
svn_non_interactive = [args.svn_command, '--non-interactive']
lib_dirpath = os.path.join(get_blender_git_root(), '..', 'lib')
svn_url = make_utils.svn_libraries_base_url(release_version, args.svn_branch)
# Checkout precompiled libraries
architecture = get_effective_architecture(args)
if sys.platform == 'darwin':
if architecture == 'arm64':
lib_platform = "darwin_arm64"
elif architecture == 'x86_64':
lib_platform = "darwin"
else:
lib_platform = None
elif sys.platform == 'win32':
# Windows checkout is usually handled by bat scripts since python3 to run
# this script is bundled as part of the precompiled libraries. However it
# is used by the buildbot.
lib_platform = "win64_vc15"
elif args.use_linux_libraries:
lib_platform = "linux_x86_64_glibc_228"
else: else:
# No precompiled libraries for Linux. architecture = platform.machine().lower()
lib_platform = None
if lib_platform: # Normalize the architecture name.
lib_platform_dirpath = os.path.join(lib_dirpath, lib_platform) if architecture in ("x86_64", "amd64"):
architecture = "x64"
if not os.path.exists(lib_platform_dirpath): assert (architecture in ("x64", "arm64"))
print_stage("Checking out Precompiled Libraries")
if make_utils.command_missing(args.svn_command): return architecture
sys.stderr.write("svn not found, can't checkout libraries\n")
sys.exit(1)
def get_submodule_directories(args: argparse.Namespace):
svn_url_platform = svn_url + lib_platform """
call(svn_non_interactive + ["checkout", svn_url_platform, lib_platform_dirpath]) Get list of all configured submodule directories.
"""
if args.use_tests:
lib_tests = "tests" blender_git_root = get_blender_git_root()
lib_tests_dirpath = os.path.join(lib_dirpath, lib_tests) dot_modules = blender_git_root / ".gitmodules"
if not os.path.exists(lib_tests_dirpath): if not dot_modules.exists():
print_stage("Checking out Tests") return ()
if make_utils.command_missing(args.svn_command): submodule_directories_output = check_output(
sys.stderr.write("svn not found, can't checkout tests\n") [args.git_command, "config", "--file", dot_modules, "--get-regexp", "path"])
sys.exit(1) return (Path(line.split(' ', 1)[1]) for line in submodule_directories_output.strip().splitlines())
svn_url_tests = svn_url + lib_tests
call(svn_non_interactive + ["checkout", svn_url_tests, lib_tests_dirpath]) def ensure_git_lfs(args: argparse.Namespace) -> None:
# Use `--skip-repo` to avoid creating git hooks.
lib_assets = "assets" # This is called from the `blender.git` checkout, so we don't need to install hooks there.
lib_assets_dirpath = os.path.join(lib_dirpath, lib_assets) call((args.git_command, "lfs", "install", "--skip-repo"), exit_on_error=True)
if not os.path.exists(lib_assets_dirpath):
print_stage("Checking out Assets") def update_precompiled_libraries(args: argparse.Namespace) -> None:
"""
if make_utils.command_missing(args.svn_command): Configure and update submodule for precompiled libraries
sys.stderr.write("svn not found, can't checkout assets\n")
sys.exit(1) This function detects the current host architecture and enables
corresponding submodule, and updates the submodule.
svn_url_assets = svn_url + lib_assets
call(svn_non_interactive + ["checkout", svn_url_assets, lib_assets_dirpath]) NOTE: When cross-compiling the architecture is coming from the command line
argument.
# Update precompiled libraries, assets and tests """
if not os.path.isdir(lib_dirpath): print_stage("Configuring Precompiled Libraries")
print("Library path: %r, not found, skipping" % lib_dirpath)
else: platform = get_effective_platform(args)
paths_local_and_remote = [] arch = get_effective_architecture(args)
if os.path.exists(os.path.join(lib_dirpath, ".svn")):
print_stage("Updating Precompiled Libraries, Assets and Tests (one repository)") print(f"Detected platform : {platform}")
paths_local_and_remote.append((lib_dirpath, svn_url)) print(f"Detected architecture : {arch}")
else: print()
print_stage("Updating Precompiled Libraries, Assets and Tests (multiple repositories)")
# Separate paths checked out. if sys.platform == "linux" and not args.use_linux_libraries:
for dirname in os.listdir(lib_dirpath): print("Skipping Linux libraries configuration")
if dirname.startswith("."): return
# Temporary paths such as ".mypy_cache" will report a warning, skip hidden directories.
continue submodule_dir = f"lib/{platform}_{arch}"
dirpath = os.path.join(lib_dirpath, dirname) submodule_directories = get_submodule_directories(args)
if not (os.path.isdir(dirpath) and os.path.exists(os.path.join(dirpath, ".svn"))):
continue if Path(submodule_dir) not in submodule_directories:
print("Skipping libraries update: no configured submodule")
paths_local_and_remote.append((dirpath, svn_url + dirname)) return
if paths_local_and_remote: make_utils.git_enable_submodule(args.git_command, submodule_dir)
if make_utils.command_missing(args.svn_command): make_utils.git_update_submodule(args.git_command, submodule_dir)
sys.stderr.write("svn not found, can't update libraries\n")
sys.exit(1)
def update_tests_data_files(args: argparse.Namespace) -> None:
for dirpath, svn_url_full in paths_local_and_remote: """
call(svn_non_interactive + ["cleanup", dirpath]) Configure and update submodule with files used by regression tests
# Switch to appropriate branch and update. """
call(svn_non_interactive + ["switch", svn_url_full, dirpath], exit_on_error=False)
call(svn_non_interactive + ["update", dirpath]) print_stage("Configuring Tests Data Files")
submodule_dir = "tests/data"
make_utils.git_enable_submodule(args.git_command, submodule_dir)
make_utils.git_update_submodule(args.git_command, submodule_dir)
# Test if git repo can be updated.
def git_update_skip(args: argparse.Namespace, check_remote_exists: bool = True) -> str: def git_update_skip(args: argparse.Namespace, check_remote_exists: bool = True) -> str:
"""Test if git repo can be updated."""
if make_utils.command_missing(args.git_command): if make_utils.command_missing(args.git_command):
sys.stderr.write("git not found, can't update code\n") sys.stderr.write("git not found, can't update code\n")
sys.exit(1) sys.exit(1)
@ -274,23 +275,22 @@ def resolve_external_url(blender_url: str, repo_name: str) -> str:
return urljoin(blender_url + "/", "../" + repo_name) return urljoin(blender_url + "/", "../" + repo_name)
def external_script_copy_old_submodule_over(args: argparse.Namespace, directory_name: str) -> None: def external_script_copy_old_submodule_over(
blender_git_root = Path(get_blender_git_root()) args: argparse.Namespace,
scripts_dir = blender_git_root / "scripts" directory: Path,
external_dir = scripts_dir / directory_name old_submodules_dir: Path) -> None:
blender_git_root = get_blender_git_root()
external_dir = blender_git_root / directory
old_submodule_relative_dir = Path("release") / "scripts" / directory_name print(f"Moving {old_submodules_dir} to {directory} ...")
print(f"Moving {old_submodule_relative_dir} to scripts/{directory_name} ...") shutil.move(blender_git_root / old_submodules_dir, external_dir)
old_submodule_dir = blender_git_root / old_submodule_relative_dir
shutil.move(old_submodule_dir, external_dir)
# Remove old ".git" which is a file with path to a submodule bare repo inside of main # Remove old ".git" which is a file with path to a submodule bare repo inside of main
# repo .git/modules directory. # repo .git/modules directory.
(external_dir / ".git").unlink() (external_dir / ".git").unlink()
bare_repo_relative_dir = Path(".git") / "modules" / "release" / "scripts" / directory_name bare_repo_relative_dir = Path(".git") / "modules" / old_submodules_dir
print(f"Copying {bare_repo_relative_dir} to scripts/{directory_name}/.git ...") print(f"Copying {bare_repo_relative_dir} to {directory}/.git ...")
bare_repo_dir = blender_git_root / bare_repo_relative_dir bare_repo_dir = blender_git_root / bare_repo_relative_dir
shutil.copytree(bare_repo_dir, external_dir / ".git") shutil.copytree(bare_repo_dir, external_dir / ".git")
@ -298,25 +298,26 @@ def external_script_copy_old_submodule_over(args: argparse.Namespace, directory_
call((args.git_command, "config", "--file", str(git_config), "--unset", "core.worktree")) call((args.git_command, "config", "--file", str(git_config), "--unset", "core.worktree"))
def external_script_initialize_if_needed(args: argparse.Namespace, def floating_checkout_initialize_if_needed(args: argparse.Namespace,
repo_name: str, repo_name: str,
directory_name: str) -> None: directory: Path,
"""Initialize checkout of an external repository scripts directory""" old_submodules_dir: Path = None) -> None:
"""Initialize checkout of an external repository"""
blender_git_root = Path(get_blender_git_root()) blender_git_root = get_blender_git_root()
blender_dot_git = blender_git_root / ".git" blender_dot_git = blender_git_root / ".git"
scripts_dir = blender_git_root / "scripts" external_dir = blender_git_root / directory
external_dir = scripts_dir / directory_name
if external_dir.exists(): if external_dir.exists():
return return
print(f"Initializing scripts/{directory_name} ...") print(f"Initializing {directory} ...")
old_submodule_dot_git = blender_git_root / "release" / "scripts" / directory_name / ".git" if old_submodules_dir is not None:
if old_submodule_dot_git.exists() and blender_dot_git.is_dir(): old_submodule_dot_git = blender_git_root / old_submodules_dir / ".git"
external_script_copy_old_submodule_over(args, directory_name) if old_submodule_dot_git.exists() and blender_dot_git.is_dir():
return external_script_copy_old_submodule_over(args, directory, old_submodules_dir)
return
origin_name = "upstream" if use_upstream_workflow(args) else "origin" origin_name = "upstream" if use_upstream_workflow(args) else "origin"
blender_url = make_utils.git_get_remote_url(args.git_command, origin_name) blender_url = make_utils.git_get_remote_url(args.git_command, origin_name)
@ -330,9 +331,9 @@ def external_script_initialize_if_needed(args: argparse.Namespace,
call((args.git_command, "clone", "--origin", origin_name, external_url, str(external_dir))) call((args.git_command, "clone", "--origin", origin_name, external_url, str(external_dir)))
def external_script_add_origin_if_needed(args: argparse.Namespace, def floating_checkout_add_origin_if_needed(args: argparse.Namespace,
repo_name: str, repo_name: str,
directory_name: str) -> None: directory: Path) -> None:
""" """
Add remote called 'origin' if there is a fork of the external repository available Add remote called 'origin' if there is a fork of the external repository available
@ -344,9 +345,8 @@ def external_script_add_origin_if_needed(args: argparse.Namespace,
cwd = os.getcwd() cwd = os.getcwd()
blender_git_root = Path(get_blender_git_root()) blender_git_root = get_blender_git_root()
scripts_dir = blender_git_root / "scripts" external_dir = blender_git_root / directory
external_dir = scripts_dir / directory_name
origin_blender_url = make_utils.git_get_remote_url(args.git_command, "origin") origin_blender_url = make_utils.git_get_remote_url(args.git_command, "origin")
origin_external_url = resolve_external_url(origin_blender_url, repo_name) origin_external_url = resolve_external_url(origin_blender_url, repo_name)
@ -361,7 +361,7 @@ def external_script_add_origin_if_needed(args: argparse.Namespace,
if not make_utils.git_is_remote_repository(args.git_command, origin_external_url): if not make_utils.git_is_remote_repository(args.git_command, origin_external_url):
return return
print(f"Adding origin remote to {directory_name} pointing to fork ...") print(f"Adding origin remote to {directory} pointing to fork ...")
# Non-obvious tricks to introduce the new remote called "origin" to the existing # Non-obvious tricks to introduce the new remote called "origin" to the existing
# submodule configuration. # submodule configuration.
@ -390,23 +390,30 @@ def external_script_add_origin_if_needed(args: argparse.Namespace,
return return
def external_scripts_update(args: argparse.Namespace, def floating_checkout_update(args: argparse.Namespace,
repo_name: str, repo_name: str,
directory_name: str, directory: Path,
branch: Optional[str]) -> str: branch: Optional[str],
old_submodules_dir: Path = None,
only_update=False) -> str:
"""Update a single external checkout with the given name in the scripts folder""" """Update a single external checkout with the given name in the scripts folder"""
external_script_initialize_if_needed(args, repo_name, directory_name) blender_git_root = get_blender_git_root()
external_script_add_origin_if_needed(args, repo_name, directory_name) external_dir = blender_git_root / directory
print(f"Updating scripts/{directory_name} ...") if only_update and not external_dir.exists():
return ""
floating_checkout_initialize_if_needed(args, repo_name, directory, old_submodules_dir)
floating_checkout_add_origin_if_needed(args, repo_name, directory)
blender_git_root = get_blender_git_root()
external_dir = blender_git_root / directory
print(f"* Updating {directory} ...")
cwd = os.getcwd() cwd = os.getcwd()
blender_git_root = Path(get_blender_git_root())
scripts_dir = blender_git_root / "scripts"
external_dir = scripts_dir / directory_name
# Update externals to appropriate given branch, falling back to main if none is given and/or # Update externals to appropriate given branch, falling back to main if none is given and/or
# found in a sub-repository. # found in a sub-repository.
branch_fallback = "main" branch_fallback = "main"
@ -419,7 +426,7 @@ def external_scripts_update(args: argparse.Namespace,
os.chdir(external_dir) os.chdir(external_dir)
msg = git_update_skip(args, check_remote_exists=False) msg = git_update_skip(args, check_remote_exists=False)
if msg: if msg:
skip_msg += directory_name + " skipped: " + msg + "\n" skip_msg += str(directory) + " skipped: " + msg + "\n"
else: else:
# Find a matching branch that exists. # Find a matching branch that exists.
for remote in ("origin", "upstream"): for remote in ("origin", "upstream"):
@ -465,6 +472,17 @@ def external_scripts_update(args: argparse.Namespace,
return skip_msg return skip_msg
def external_scripts_update(args: argparse.Namespace,
repo_name: str,
directory_name: str,
branch: Optional[str]) -> str:
return floating_checkout_update(args,
repo_name,
Path("scripts") / directory_name,
branch,
old_submodules_dir=Path("release") / "scripts" / directory_name)
def scripts_submodules_update(args: argparse.Namespace, branch: Optional[str]) -> str: def scripts_submodules_update(args: argparse.Namespace, branch: Optional[str]) -> str:
"""Update working trees of addons and addons_contrib within the scripts/ directory""" """Update working trees of addons and addons_contrib within the scripts/ directory"""
msg = "" msg = ""
@ -475,12 +493,74 @@ def scripts_submodules_update(args: argparse.Namespace, branch: Optional[str]) -
return msg return msg
def floating_libraries_update(args: argparse.Namespace, branch: Optional[str]) -> str:
"""Update libraries checkouts which are floating (not attached as Git submodules)"""
msg = ""
msg += floating_checkout_update(args,
"benchmarks",
Path("tests") / "benchmarks",
branch,
only_update=True)
return msg
def add_submodule_push_url(args: argparse.Namespace):
"""
Add pushURL configuration for all locally activated submodules, pointing to SSH protocol.
"""
blender_git_root = get_blender_git_root()
modules = blender_git_root / ".git" / "modules"
submodule_directories = get_submodule_directories(args)
for submodule_path in submodule_directories:
module_path = modules / submodule_path
config = module_path / "config"
if not config.exists():
# Ignore modules which are not initialized
continue
push_url = check_output((args.git_command, "config", "--file", str(config),
"--get", "remote.origin.pushURL"), exit_on_error=False)
if push_url and push_url != "git@projects.blender.org:blender/lib-darwin_arm64.git":
# Ignore modules which have pushURL configured.
# Keep special exception, as some debug code sneaked into the production for a short
# while.
continue
url = make_utils.git_get_config(args.git_command, "remote.origin.url", str(config))
if not url.startswith("https:"):
# Ignore non-URL URLs.
continue
url_parts = urlsplit(url)
push_url = f"git@{url_parts.netloc}:{url_parts.path[1:]}"
print(f"Setting pushURL to {push_url} for {submodule_path}")
make_utils.git_set_config(args.git_command, "remote.origin.pushURL", push_url, str(config))
def submodules_update(args: argparse.Namespace, branch: Optional[str]) -> str: def submodules_update(args: argparse.Namespace, branch: Optional[str]) -> str:
"""Update submodules or other externally tracked source trees""" """Update submodules or other externally tracked source trees"""
print_stage("Updating Submodules")
msg = "" msg = ""
msg += scripts_submodules_update(args, branch) msg += scripts_submodules_update(args, branch)
msg += floating_libraries_update(args, branch)
print("* Updating Git submodules")
exitcode = call((args.git_command, "submodule", "update", "--init"), exit_on_error=False)
if exitcode != 0:
msg += "Error updating Git submodules\n"
add_submodule_push_url(args)
return msg return msg
@ -494,26 +574,33 @@ if __name__ == "__main__":
major = blender_version.version // 100 major = blender_version.version // 100
minor = blender_version.version % 100 minor = blender_version.version % 100
branch = f"blender-v{major}.{minor}-release" branch = f"blender-v{major}.{minor}-release"
release_version: Optional[str] = f"{major}.{minor}"
else: else:
branch = 'main' branch = 'main'
release_version = None
if not args.no_libraries: # Submodules and precompiled libraries require Git LFS.
svn_update(args, release_version) ensure_git_lfs(args)
if not args.no_blender: if not args.no_blender:
blender_skip_msg = git_update_skip(args) blender_skip_msg = git_update_skip(args)
if not blender_skip_msg: if not blender_skip_msg:
blender_skip_msg = blender_update(args) blender_skip_msg = blender_update(args)
if blender_skip_msg: if blender_skip_msg:
blender_skip_msg = "Blender repository skipped: " + blender_skip_msg + "\n" blender_skip_msg = "Blender repository skipped: " + blender_skip_msg + "\n"
if not args.no_libraries:
update_precompiled_libraries(args)
if args.use_tests:
update_tests_data_files(args)
if not args.no_submodules: if not args.no_submodules:
submodules_skip_msg = submodules_update(args, branch) submodules_skip_msg = submodules_update(args, branch)
# Report any skipped repositories at the end, so it's not as easy to miss. # Report any skipped repositories at the end, so it's not as easy to miss.
skip_msg = blender_skip_msg + submodules_skip_msg skip_msg = blender_skip_msg + submodules_skip_msg
if skip_msg: if skip_msg:
print_stage(skip_msg.strip()) print()
print(skip_msg.strip())
print()
# For failed submodule update we throw an error, since not having correct # For failed submodule update we throw an error, since not having correct
# submodules can make Blender throw errors. # submodules can make Blender throw errors.

View File

@ -11,9 +11,7 @@ import re
import shutil import shutil
import subprocess import subprocess
import sys import sys
import os
from pathlib import Path from pathlib import Path
from urllib.parse import urljoin
from typing import ( from typing import (
Sequence, Sequence,
@ -48,7 +46,7 @@ def check_output(cmd: Sequence[str], exit_on_error: bool = True) -> str:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True) output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if exit_on_error: if exit_on_error:
sys.stderr.write(" ".join(cmd)) sys.stderr.write(" ".join(cmd) + "\n")
sys.stderr.write(e.output + "\n") sys.stderr.write(e.output + "\n")
sys.exit(e.returncode) sys.exit(e.returncode)
output = "" output = ""
@ -87,25 +85,6 @@ def git_remote_exist(git_command: str, remote_name: str) -> bool:
return remote_url != remote_name return remote_url != remote_name
def git_get_resolved_submodule_url(git_command: str, blender_url: str, submodule_path: str) -> str:
git_root = check_output([git_command, "rev-parse", "--show-toplevel"])
dot_gitmodules = os.path.join(git_root, ".gitmodules")
submodule_key_prefix = f"submodule.{submodule_path}"
submodule_key_url = f"{submodule_key_prefix}.url"
gitmodule_url = git_get_config(
git_command, submodule_key_url, file=dot_gitmodules)
# A bit of a trickery to construct final URL.
# Only works for the relative submodule URLs.
#
# Note that unless the LHS URL ends up with a slash urljoin treats the last component as a
# file.
assert gitmodule_url.startswith('..')
return urljoin(blender_url + "/", gitmodule_url)
def git_is_remote_repository(git_command: str, repo: str) -> bool: def git_is_remote_repository(git_command: str, repo: str) -> bool:
"""Returns true if the given repository is a valid/clonable git repo""" """Returns true if the given repository is a valid/clonable git repo"""
exit_code = call((git_command, "ls-remote", repo, "HEAD"), exit_on_error=False, silent=True) exit_code = call((git_command, "ls-remote", repo, "HEAD"), exit_on_error=False, silent=True)
@ -113,7 +92,8 @@ def git_is_remote_repository(git_command: str, repo: str) -> bool:
def git_branch(git_command: str) -> str: def git_branch(git_command: str) -> str:
# Get current branch name. """Get current branch name."""
try: try:
branch = subprocess.check_output([git_command, "rev-parse", "--abbrev-ref", "HEAD"]) branch = subprocess.check_output([git_command, "rev-parse", "--abbrev-ref", "HEAD"])
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
@ -137,44 +117,32 @@ def git_set_config(git_command: str, key: str, value: str, file: Optional[str] =
return check_output([git_command, "config", key, value]) return check_output([git_command, "config", key, value])
def git_tag(git_command: str) -> Optional[str]: def git_enable_submodule(git_command: str, submodule_dir: str):
# Get current tag name. """Enable submodule denoted by its directory within the repository"""
try:
tag = subprocess.check_output([git_command, "describe", "--exact-match"], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
return None
return tag.strip().decode('utf8') command = (git_command,
"config",
"--local",
f"submodule.{submodule_dir}.update", "checkout")
call(command, exit_on_error=True, silent=False)
def git_branch_release_version(branch: str, tag: Optional[str]) -> Optional[str]: def git_update_submodule(git_command: str, submodule_dir: str):
re_match = re.search("^blender-v(.*)-release$", branch) """
release_version = None Update the given submodule.
if re_match:
release_version = re_match.group(1)
elif tag:
re_match = re.search(r"^v([0-9]*\.[0-9]*).*", tag)
if re_match:
release_version = re_match.group(1)
return release_version
The submodule is denoted by its path within the repository.
This function will initialize the submodule if it has not been initialized.
"""
def svn_libraries_base_url(release_version: Optional[str], branch: Optional[str] = None) -> str: call((git_command, "submodule", "update", "--init", submodule_dir))
if release_version:
svn_branch = "tags/blender-" + release_version + "-release"
elif branch:
svn_branch = "branches/" + branch
else:
svn_branch = "trunk"
return "https://svn.blender.org/svnroot/bf-blender/" + svn_branch + "/lib/"
def command_missing(command: str) -> bool: def command_missing(command: str) -> bool:
# Support running with Python 2 for macOS # Support running with Python 2 for macOS
if sys.version_info >= (3, 0): if sys.version_info >= (3, 0):
return shutil.which(command) is None return shutil.which(command) is None
else: return False
return False
class BlenderVersion: class BlenderVersion:

View File

@ -1,37 +1,30 @@
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15 set BUILD_VS_LIBDIR="lib\windows_x64"
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
set BUILD_VS_SVNDIR=win64_%BUILD_VS_LIBDIRPOST%
set BUILD_VS_LIBDIR="%BLENDER_DIR%..\lib\%BUILD_VS_SVNDIR%"
if NOT "%verbose%" == "" ( if NOT "%verbose%" == "" (
echo Library Directory = "%BUILD_VS_LIBDIR%" echo Library Directory = "%BUILD_VS_LIBDIR%"
) )
if NOT EXIST %BUILD_VS_LIBDIR% ( if NOT EXIST "%BUILD_VS_LIBDIR%\.git" (
rem libs not found, but svn is on the system rem libs not found, but git is on the system
if not "%SVN%"=="" ( if not "%GIT%"=="" (
echo. echo.
echo The required external libraries in %BUILD_VS_LIBDIR% are missing echo The required external libraries in %BUILD_VS_LIBDIR% are missing
echo. echo.
set /p GetLibs= "Would you like to download them? (y/n)" set /p GetLibs= "Would you like to download them? (y/n)"
if /I "!GetLibs!"=="Y" ( if /I "!GetLibs!"=="Y" (
echo. echo.
echo Downloading %BUILD_VS_SVNDIR% libraries, please wait. echo Downloading %BUILD_VS_LIBDIR% libraries, please wait.
echo. echo.
:RETRY :RETRY
"%SVN%" checkout https://svn.blender.org/svnroot/bf-blender/trunk/lib/%BUILD_VS_SVNDIR% %BUILD_VS_LIBDIR% "%GIT%" -C "%BLENDER_DIR%" config --local "submodule.%BUILD_VS_LIBDIR%.update" "checkout"
"%GIT%" -C "%BLENDER_DIR%" submodule update --init "%BUILD_VS_LIBDIR%"
if errorlevel 1 ( if errorlevel 1 (
set /p LibRetry= "Error during download, retry? y/n" set /p LibRetry= "Error during download, retry? y/n"
if /I "!LibRetry!"=="Y" ( if /I "!LibRetry!"=="Y" (
cd %BUILD_VS_LIBDIR%
"%SVN%" cleanup
cd %BLENDER_DIR%
goto RETRY goto RETRY
) )
echo. echo.
echo Error: Download of external libraries failed. echo Error: Download of external libraries failed.
echo This is needed for building, please manually run 'svn cleanup' and 'svn update' in echo Until this is resolved you CANNOT make a successful blender build.
echo %BUILD_VS_LIBDIR% , until this is resolved you CANNOT make a successful blender build
echo. echo.
exit /b 1 exit /b 1
) )
@ -39,11 +32,11 @@ if NOT EXIST %BUILD_VS_LIBDIR% (
) )
) else ( ) else (
if NOT EXIST %PYTHON% ( if NOT EXIST %PYTHON% (
if not "%SVN%"=="" ( if not "%GIT%"=="" (
echo. echo.
echo Python not found in external libraries, updating to latest version echo Python not found in external libraries, updating to latest version
echo. echo.
"%SVN%" update %BUILD_VS_LIBDIR% "%GIT%" -C "%BLENDER_DIR%" submodule update "%BUILD_VS_LIBDIR%"
) )
) )
) )
@ -53,8 +46,8 @@ if NOT EXIST %BUILD_VS_LIBDIR% (
echo Error: Required libraries not found at "%BUILD_VS_LIBDIR%" echo Error: Required libraries not found at "%BUILD_VS_LIBDIR%"
echo This is needed for building, aborting! echo This is needed for building, aborting!
echo. echo.
if "%SVN%"=="" ( if "%GIT%"=="" (
echo This is most likely caused by svn.exe not being available. echo This is most likely caused by git.exe not being available.
) )
exit /b 1 exit /b 1
) )

View File

@ -1,5 +1,4 @@
REM find all dependencies and set the corresponding environment variables. REM find all dependencies and set the corresponding environment variables.
for %%X in (svn.exe) do (set SVN=%%~$PATH:X)
for %%X in (cmake.exe) do (set CMAKE=%%~$PATH:X) for %%X in (cmake.exe) do (set CMAKE=%%~$PATH:X)
for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X) for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X)
for %%X in (git.exe) do (set GIT=%%~$PATH:X) for %%X in (git.exe) do (set GIT=%%~$PATH:X)
@ -7,19 +6,19 @@ REM For python, default on 310 but if that does not exist also check
REM the 311, 312 and finally 39 folders to see if those are there, it checks REM the 311, 312 and finally 39 folders to see if those are there, it checks
REM this far ahead to ensure good lib folder compatibility in the future REM this far ahead to ensure good lib folder compatibility in the future
REM it falls back to 3.9 just incase it is a very old lib folder. REM it falls back to 3.9 just incase it is a very old lib folder.
set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\310\bin\python.exe set PYTHON=%BLENDER_DIR%\lib\windows_x64\python\310\bin\python.exe
if EXIST %PYTHON% ( if EXIST %PYTHON% (
goto detect_python_done goto detect_python_done
) )
set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\311\bin\python.exe set PYTHON=%BLENDER_DIR%\lib\windows_x64\python\311\bin\python.exe
if EXIST %PYTHON% ( if EXIST %PYTHON% (
goto detect_python_done goto detect_python_done
) )
set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\312\bin\python.exe set PYTHON=%BLENDER_DIR%\lib\windows_x64\python\312\bin\python.exe
if EXIST %PYTHON% ( if EXIST %PYTHON% (
goto detect_python_done goto detect_python_done
) )
set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe set PYTHON=%BLENDER_DIR%\lib\windows_x64\python\39\bin\python.exe
if EXIST %PYTHON% ( if EXIST %PYTHON% (
goto detect_python_done goto detect_python_done
) )
@ -36,7 +35,6 @@ if NOT EXIST %PYTHON% (
:detect_python_done :detect_python_done
if NOT "%verbose%" == "" ( if NOT "%verbose%" == "" (
echo svn : "%SVN%"
echo cmake : "%CMAKE%" echo cmake : "%CMAKE%"
echo ctest : "%CTEST%" echo ctest : "%CTEST%"
echo git : "%GIT%" echo git : "%GIT%"

View File

@ -1,5 +1,5 @@
if EXIST %BLENDER_DIR%\..\lib\win64_vc15\llvm\bin\clang-format.exe ( if EXIST %BLENDER_DIR%\lib\windows_x64\llvm\bin\clang-format.exe (
set CF_PATH=..\lib\win64_vc15\llvm\bin set CF_PATH=lib\windows_x64\llvm\bin
goto detect_done goto detect_done
) )

View File

@ -0,0 +1,16 @@
set BUILD_VS_LIBDIR="lib\windows_x64"
:RETRY
"%GIT%" -C "%BLENDER_DIR%" config --local "submodule.%BUILD_VS_LIBDIR%.update" "checkout"
"%GIT%" -C "%BLENDER_DIR%" submodule update --init "%BUILD_VS_LIBDIR%"
if errorlevel 1 (
set /p LibRetry= "Error during update, retry? y/n"
if /I "!LibRetry!"=="Y" (
goto RETRY
)
echo.
echo Error: Download of external libraries failed.
echo Until this is resolved you CANNOT make a successful blender build.
echo.
exit /b 1
)

View File

@ -110,9 +110,6 @@ if NOT "%1" == "" (
) else if "%1" == "doc_py" ( ) else if "%1" == "doc_py" (
set DOC_PY=1 set DOC_PY=1
goto EOF goto EOF
) else if "%1" == "svnfix" (
set SVN_FIX=1
goto EOF
) else ( ) else (
echo Command "%1" unknown, aborting! echo Command "%1" unknown, aborting!
goto ERR goto ERR

View File

@ -4,9 +4,7 @@ set BUILD_CMAKE_ARGS=
set BUILD_ARCH= set BUILD_ARCH=
set BUILD_VS_VER= set BUILD_VS_VER=
set BUILD_VS_YEAR= set BUILD_VS_YEAR=
set BUILD_VS_LIBDIRPOST=
set BUILD_VS_LIBDIR= set BUILD_VS_LIBDIR=
set BUILD_VS_SVNDIR=
set KEY_NAME= set KEY_NAME=
set MSBUILD_PLATFORM= set MSBUILD_PLATFORM=
set MUST_CLEAN= set MUST_CLEAN=

View File

@ -8,13 +8,10 @@ for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Branch_hash=%%i
cd "%BLENDER_DIR%/scripts/addons" cd "%BLENDER_DIR%/scripts/addons"
for /f "delims=" %%i in ('"%GIT%" rev-parse --abbrev-ref HEAD') do echo Addons_Branch_name=%%i for /f "delims=" %%i in ('"%GIT%" rev-parse --abbrev-ref HEAD') do echo Addons_Branch_name=%%i
for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Addons_Branch_hash=%%i for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Addons_Branch_hash=%%i
if "%SVN%" == "" (
echo SVN not found, cannot library information. cd "%BLENDER_DIR%/lib/windows_x64"
goto EOF for /f "delims=" %%i in ('"%GIT%" rev-parse --abbrev-ref HEAD') do echo Libs_Branch_name=%%i
) for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Libs_Branch_hash=%%i
set BUILD_VS_LIBDIR=%BLENDER_DIR%..\lib\win64_vc15
for /f "delims=" %%i in ('"%SVN%" info --show-item=url --no-newline %BUILD_VS_LIBDIR% ') do echo Libs_URL=%%i
for /f "delims=" %%i in ('"%SVN%" info --show-item=revision --no-newline %BUILD_VS_LIBDIR% ') do echo Libs_Revision=%%i
for /f "delims=" %%i in ('"%SVN%" info --show-item=last-changed-date --no-newline %BUILD_VS_LIBDIR% ') do echo Libs_LastChange=%%i
cd "%BLENDER_DIR%" cd "%BLENDER_DIR%"
:EOF :EOF

View File

@ -1,25 +0,0 @@
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
set BUILD_VS_SVNDIR=win64_%BUILD_VS_LIBDIRPOST%
set BUILD_VS_LIBDIR="%BLENDER_DIR%..\lib\%BUILD_VS_SVNDIR%"
echo Starting cleanup in %BUILD_VS_LIBDIR%.
cd %BUILD_VS_LIBDIR%
:RETRY
"%SVN%" cleanup
"%SVN%" update
if errorlevel 1 (
set /p LibRetry= "Error during update, retry? y/n"
if /I "!LibRetry!"=="Y" (
goto RETRY
)
echo.
echo Error: Download of external libraries failed.
echo This is needed for building, please manually run 'svn cleanup' and 'svn update' in
echo %BUILD_VS_LIBDIR% , until this is resolved you CANNOT make a successful blender build
echo.
exit /b 1
)
echo Cleanup complete

View File

@ -1,24 +0,0 @@
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
set BUILD_VS_SVNDIR=win64_%BUILD_VS_LIBDIRPOST%
set BUILD_VS_LIBDIR="%BLENDER_DIR%..\lib\%BUILD_VS_SVNDIR%"
cd %BUILD_VS_LIBDIR%
:RETRY
"%SVN%" update
if errorlevel 1 (
set /p LibRetry= "Error during update, retry? y/n"
if /I "!LibRetry!"=="Y" (
"%SVN%" cleanup
goto RETRY
)
echo.
echo Error: Download of external libraries failed.
echo This is needed for building, please manually run 'svn cleanup' and 'svn update' in
echo %BUILD_VS_LIBDIR% , until this is resolved you CANNOT make a successful blender build
echo.
exit /b 1
)
cd %BLENDER_DIR%

View File

@ -8,6 +8,6 @@ exit /b 1
:detect_python_done :detect_python_done
REM Use -B to avoid writing __pycache__ in lib directory and causing update conflicts. REM Use -B to avoid writing __pycache__ in lib directory and causing update conflicts.
%PYTHON% -B %BLENDER_DIR%\build_files\utils\make_test.py --git-command "%GIT%" --svn-command "%SVN%" --cmake-command="%CMAKE%" --ctest-command="%CTEST%" --config="%BUILD_TYPE%" %BUILD_DIR% %PYTHON% -B %BLENDER_DIR%\build_files\utils\make_test.py --git-command "%GIT%" --cmake-command="%CMAKE%" --ctest-command="%CTEST%" --config="%BUILD_TYPE%" %BUILD_DIR%
:EOF :EOF

View File

@ -5,6 +5,6 @@ if NOT EXIST %PYTHON% (
:detect_python_done :detect_python_done
REM Use -B to avoid writing __pycache__ in lib directory and causing update conflicts. REM Use -B to avoid writing __pycache__ in lib directory and causing update conflicts.
%PYTHON% -B %BLENDER_DIR%\build_files\utils\make_update.py --git-command "%GIT%" --svn-command "%SVN%" %BUILD_UPDATE_ARGS% %PYTHON% -B %BLENDER_DIR%\build_files\utils\make_update.py --git-command "%GIT%" %BUILD_UPDATE_ARGS%
:EOF :EOF

View File

@ -470,7 +470,7 @@ ccl_device Spectrum bsdf_hair_huang_eval_residual(KernelGlobals kg,
const float3 wh1 = sample_wh(kg, roughness, wi, wmi, sample1); const float3 wh1 = sample_wh(kg, roughness, wi, wmi, sample1);
const float cos_hi1 = dot(wi, wh1); const float cos_hi1 = dot(wi, wh1);
if (!(cos_hi1 > 0)) { if (!(cos_hi1 > 0.0f)) {
continue; continue;
} }
@ -535,7 +535,7 @@ ccl_device Spectrum bsdf_hair_huang_eval_residual(KernelGlobals kg,
lcg_step_float(&rng_quadrature)); lcg_step_float(&rng_quadrature));
const float3 wh2 = sample_wh(kg, roughness, -wt, wmt, sample2); const float3 wh2 = sample_wh(kg, roughness, -wt, wmt, sample2);
const float cos_hi2 = dot(-wt, wh2); const float cos_hi2 = dot(-wt, wh2);
if (!(cos_hi2 > 0)) { if (!(cos_hi2 > 0.0f)) {
continue; continue;
} }
const float R2 = fresnel_dielectric_cos(cos_hi2, inv_eta); const float R2 = fresnel_dielectric_cos(cos_hi2, inv_eta);
@ -717,7 +717,7 @@ ccl_device int bsdf_hair_huang_sample(const KernelGlobals kg,
wtt = refract_angle(-wt, wh2, cos_theta_t2, bsdf->eta); wtt = refract_angle(-wt, wh2, cos_theta_t2, bsdf->eta);
if (dot(wmt, -wtt) > 0.0f && cos_theta_t2 != 0.0f && microfacet_visible(-wtt, wmt_, wh2)) { if (dot(wmt, -wtt) > 0.0f && T2 > 0.0f && microfacet_visible(-wtt, wmt_, wh2)) {
TT = bsdf->extra->TT * T1 * A_t * T2 * scale2 * bsdf_Go(roughness2, cos_mi2, dot(wmt, -wtt)); TT = bsdf->extra->TT * T1 * A_t * T2 * scale2 * bsdf_Go(roughness2, cos_mi2, dot(wmt, -wtt));
} }
@ -746,9 +746,7 @@ ccl_device int bsdf_hair_huang_sample(const KernelGlobals kg,
const float T3 = 1.0f - R3; const float T3 = 1.0f - R3;
if (cos_theta_t3 != 0.0f && if (T3 > 0.0f && microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.0f, wmtr.z), wh3)) {
microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.0f, wmtr.z), wh3))
{
TRT = bsdf->extra->TRT * TR * make_spectrum(T3) * TRT = bsdf->extra->TRT * TR * make_spectrum(T3) *
bsdf_Go(roughness2, cos_mi3, dot(wmtr, -wtrt)); bsdf_Go(roughness2, cos_mi3, dot(wmtr, -wtrt));
} }
@ -760,8 +758,8 @@ ccl_device int bsdf_hair_huang_sample(const KernelGlobals kg,
/* Sample `theta_o`. */ /* Sample `theta_o`. */
const float rand_theta = max(lcg_step_float(&sd->lcg_state), 1e-5f); const float rand_theta = max(lcg_step_float(&sd->lcg_state), 1e-5f);
const float fac = 1.0f + const float fac = 1.0f +
bsdf->roughness * 4.0f * bsdf->roughness *
logf(rand_theta + (1.0f - rand_theta) * expf(-2.0f / bsdf->roughness)); logf(rand_theta + (1.0f - rand_theta) * expf(-0.5f / bsdf->roughness));
const float sin_theta_o = -fac * sin_theta(wi) + const float sin_theta_o = -fac * sin_theta(wi) +
cos_from_sin(fac) * cos_from_sin(fac) *
cosf(M_2PI_F * lcg_step_float(&sd->lcg_state)) * cos_theta(wi); cosf(M_2PI_F * lcg_step_float(&sd->lcg_state)) * cos_theta(wi);

View File

@ -135,18 +135,26 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
const float3 e1 = V[2] - V[0]; const float3 e1 = V[2] - V[0];
const float3 e2 = V[2] - V[1]; const float3 e2 = V[2] - V[1];
const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2)));
const float3 N0 = cross(e0, e1); float3 N0 = cross(e0, e1);
/* Flip normal if necessary. */
const int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_NEGATIVE_SCALE) {
N0 = -N0;
}
/* Do not draw samples from the side without MIS. */
ls->shader = kernel_data_fetch(tri_shader, prim);
const float distance_to_plane = dot(N0, V[0] - P) / dot(N0, N0);
const int ls_shader_flag = kernel_data_fetch(shaders, ls->shader & SHADER_MASK).flags;
if (!(ls_shader_flag & (distance_to_plane > 0 ? SD_MIS_BACK : SD_MIS_FRONT))) {
return false;
}
float Nl = 0.0f; float Nl = 0.0f;
ls->Ng = safe_normalize_len(N0, &Nl); ls->Ng = safe_normalize_len(N0, &Nl);
const float area = 0.5f * Nl; const float area = 0.5f * Nl;
/* flip normal if necessary */
const int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_NEGATIVE_SCALE) {
ls->Ng = -ls->Ng;
}
ls->eval_fac = 1.0f; ls->eval_fac = 1.0f;
ls->shader = kernel_data_fetch(tri_shader, prim);
ls->object = object; ls->object = object;
ls->prim = prim; ls->prim = prim;
ls->lamp = LAMP_NONE; ls->lamp = LAMP_NONE;
@ -154,8 +162,6 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
ls->type = LIGHT_TRIANGLE; ls->type = LIGHT_TRIANGLE;
ls->group = object_lightgroup(kg, object); ls->group = object_lightgroup(kg, object);
float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0));
if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) { if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) {
/* A modified version of James Arvo, "Stratified Sampling of Spherical Triangles" /* A modified version of James Arvo, "Stratified Sampling of Spherical Triangles"
* http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */ * http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */

View File

@ -1331,7 +1331,7 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
float3 dir = safe_normalize(light->get_dir()); float3 dir = safe_normalize(light->get_dir());
if (light->use_mis && area != 0.0f) { if (light->use_mis && area != 0.0f && light->spread > 0.0f) {
shader_id |= SHADER_USE_MIS; shader_id |= SHADER_USE_MIS;
} }

1
lib/linux_x64 Submodule

@ -0,0 +1 @@
Subproject commit d2eea8a8a6b22d6ec6849deea72e141fc5b384d4

1
lib/macos_arm64 Submodule

@ -0,0 +1 @@
Subproject commit 6c8139034cfb05f8dee9f6648d31443792c160b5

1
lib/macos_x64 Submodule

@ -0,0 +1 @@
Subproject commit bf950af1a7d197675bd28ce7d4920ba2b4a0a4f6

1
lib/windows_x64 Submodule

@ -0,0 +1 @@
Subproject commit 2935423fe0636157750feef1fc02d8d0c3efd9dd

View File

@ -56,11 +56,6 @@ if "%BUILD_VS_YEAR%" == "" (
) )
) )
if "%SVN_FIX%" == "1" (
call "%BLENDER_DIR%\build_files\windows\svn_fix.cmd"
goto EOF
)
if "%BUILD_UPDATE%" == "1" ( if "%BUILD_UPDATE%" == "1" (
REM First see if the SVN libs are there and check them out if they are not. REM First see if the SVN libs are there and check them out if they are not.
call "%BLENDER_DIR%\build_files\windows\check_libraries.cmd" call "%BLENDER_DIR%\build_files\windows\check_libraries.cmd"
@ -70,7 +65,7 @@ if "%BUILD_UPDATE%" == "1" (
REM running tends to be problematic. The python script that update_sources REM running tends to be problematic. The python script that update_sources
REM calls later on may still try to switch branches and run into trouble, REM calls later on may still try to switch branches and run into trouble,
REM but for *most* people this will side step the problem. REM but for *most* people this will side step the problem.
call "%BLENDER_DIR%\build_files\windows\svn_update.cmd" call "%BLENDER_DIR%\build_files\windows\lib_update.cmd"
) )
REM Finally call the python script shared between all platforms that updates git REM Finally call the python script shared between all platforms that updates git
REM and does any other SVN work like update the tests or branch switches REM and does any other SVN work like update the tests or branch switches

@ -0,0 +1 @@
Subproject commit 38a8f38d98987efc9aebe878ca941d99756e914e

View File

@ -143,6 +143,11 @@ class DATA_PT_grease_pencil_layer_relations(LayerDataButtonsPanel, Panel):
row = layout.row(align=True) row = layout.row(align=True)
row.prop_search(layer, "parent_bone", layer.parent.data, "bones", text="Bone") row.prop_search(layer, "parent_bone", layer.parent.data, "bones", text="Bone")
layout.separator()
col = layout.row(align=True)
col.prop(layer, "pass_index")
classes = ( classes = (
DATA_PT_context_grease_pencil, DATA_PT_context_grease_pencil,

View File

@ -19,7 +19,7 @@
namespace blender::asset_system::tests { namespace blender::asset_system::tests {
/* UUIDs from lib/tests/asset_library/blender_assets.cats.txt */ /* UUIDs from tests/data/asset_library/blender_assets.cats.txt */
const bUUID UUID_ID_WITHOUT_PATH("e34dd2c5-5d2e-4668-9794-1db5de2a4f71"); const bUUID UUID_ID_WITHOUT_PATH("e34dd2c5-5d2e-4668-9794-1db5de2a4f71");
const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78");
const bUUID UUID_POSES_ELLIE_WHITESPACE("b06132f6-5687-4751-a6dd-392740eb3c46"); const bUUID UUID_POSES_ELLIE_WHITESPACE("b06132f6-5687-4751-a6dd-392740eb3c46");
@ -31,7 +31,7 @@ const bUUID UUID_POSES_RUZENA_FACE("82162c1f-06cc-4d91-a9bf-4f72c104e348");
const bUUID UUID_WITHOUT_SIMPLENAME("d7916a31-6ca9-4909-955f-182ca2b81fa3"); const bUUID UUID_WITHOUT_SIMPLENAME("d7916a31-6ca9-4909-955f-182ca2b81fa3");
const bUUID UUID_ANOTHER_RUZENA("00000000-d9fa-4b91-b704-e6af1f1339ef"); const bUUID UUID_ANOTHER_RUZENA("00000000-d9fa-4b91-b704-e6af1f1339ef");
/* UUIDs from lib/tests/asset_library/modified_assets.cats.txt */ /* UUIDs from tests/data/asset_library/modified_assets.cats.txt */
const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34"); const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34");
/* Subclass that adds accessors such that protected fields can be used in tests. */ /* Subclass that adds accessors such that protected fields can be used in tests. */

View File

@ -176,7 +176,7 @@ enum {
BLENDER_SYSTEM_PYTHON = 54, BLENDER_SYSTEM_PYTHON = 54,
}; };
/* for BKE_appdir_folder_id_version only */ /** For #BKE_appdir_folder_id_version only. */
enum { enum {
BLENDER_RESOURCE_PATH_USER = 0, BLENDER_RESOURCE_PATH_USER = 0,
BLENDER_RESOURCE_PATH_LOCAL = 1, BLENDER_RESOURCE_PATH_LOCAL = 1,

View File

@ -481,6 +481,11 @@ inline const bNode *bNodeTree::group_output_node() const
return this->runtime->group_output_node; return this->runtime->group_output_node;
} }
inline blender::Span<bNode *> bNodeTree::group_input_nodes()
{
return this->nodes_by_type("NodeGroupInput");
}
inline blender::Span<const bNode *> bNodeTree::group_input_nodes() const inline blender::Span<const bNode *> bNodeTree::group_input_nodes() const
{ {
return this->nodes_by_type("NodeGroupInput"); return this->nodes_by_type("NodeGroupInput");

View File

@ -103,6 +103,16 @@ void BKE_preferences_extension_repo_dirpath_get(const bUserExtensionRepo *repo,
bUserExtensionRepo *BKE_preferences_extension_repo_find_index(const UserDef *userdef, int index); bUserExtensionRepo *BKE_preferences_extension_repo_find_index(const UserDef *userdef, int index);
bUserExtensionRepo *BKE_preferences_extension_repo_find_by_module(const UserDef *userdef, bUserExtensionRepo *BKE_preferences_extension_repo_find_by_module(const UserDef *userdef,
const char *module); const char *module);
/**
* Using a full URL/remote path to find a repository that shares its prefix.
*/
bUserExtensionRepo *BKE_preferences_extension_repo_find_by_remote_path_prefix(
const UserDef *userdef, const char *remote_path_full, const bool only_enabled);
/**
* Skip the `https` or `http` part of a URL `https://`, return zero if none is found.
*/
int BKE_preferences_extension_repo_remote_scheme_end(const char *url);
int BKE_preferences_extension_repo_get_index(const UserDef *userdef, int BKE_preferences_extension_repo_get_index(const UserDef *userdef,
const bUserExtensionRepo *repo); const bUserExtensionRepo *repo);

View File

@ -139,8 +139,9 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports,
return nullptr; return nullptr;
} }
const int expected_length = avs->path_ef - avs->path_sf; /* Adding 1 because the avs range is inclusive on both ends. */
BLI_assert(expected_length > 0); /* Because the `if` above. */ const int expected_length = (avs->path_ef - avs->path_sf) + 1;
BLI_assert(expected_length > 1); /* Because the `if` above. */
/* If there is already a motionpath, just return that, provided its settings /* If there is already a motionpath, just return that, provided its settings
* are ok (saves extra free+alloc). */ * are ok (saves extra free+alloc). */
@ -157,7 +158,7 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports,
/* Only reuse a path if it was already a valid path, and of the expected length. */ /* Only reuse a path if it was already a valid path, and of the expected length. */
if (mpath->start_frame != mpath->end_frame && mpath->length == expected_length) { if (mpath->start_frame != mpath->end_frame && mpath->length == expected_length) {
mpath->start_frame = avs->path_sf; mpath->start_frame = avs->path_sf;
mpath->end_frame = avs->path_ef; mpath->end_frame = avs->path_ef + 1;
return mpath; return mpath;
} }
@ -171,7 +172,7 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports,
/* Copy mpath settings from the viz settings. */ /* Copy mpath settings from the viz settings. */
mpath->start_frame = avs->path_sf; mpath->start_frame = avs->path_sf;
mpath->end_frame = avs->path_ef; mpath->end_frame = avs->path_ef + 1;
mpath->length = expected_length; mpath->length = expected_length;
if (avs->path_bakeflag & MOTIONPATH_BAKE_HEADS) { if (avs->path_bakeflag & MOTIONPATH_BAKE_HEADS) {

View File

@ -45,7 +45,7 @@ IndexRange NodeBakeCache::frame_range() const
} }
const int start_frame = this->frames.first()->frame.frame(); const int start_frame = this->frames.first()->frame.frame();
const int end_frame = this->frames.last()->frame.frame(); const int end_frame = this->frames.last()->frame.frame();
return {start_frame, end_frame - start_frame + 1}; return IndexRange::from_begin_end_inclusive(start_frame, end_frame);
} }
SimulationNodeCache *ModifierCache::get_simulation_node_cache(const int id) SimulationNodeCache *ModifierCache::get_simulation_node_cache(const int id)

View File

@ -512,7 +512,7 @@ static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, con
std::swap(*new_lb, *old_lb); std::swap(*new_lb, *old_lb);
/* TODO: Could add per-IDType control over namemaps clearing, if this becomes a performances /* TODO: Could add per-IDType control over name-maps clearing, if this becomes a performances
* concern. */ * concern. */
BKE_main_namemap_clear(old_bmain); BKE_main_namemap_clear(old_bmain);
BKE_main_namemap_clear(new_bmain); BKE_main_namemap_clear(new_bmain);

View File

@ -6,6 +6,9 @@
* \ingroup bke * \ingroup bke
*/ */
#include <fmt/format.h>
#include "BKE_anim_data.h"
#include "BKE_attribute.hh" #include "BKE_attribute.hh"
#include "BKE_colorband.hh" #include "BKE_colorband.hh"
#include "BKE_colortools.hh" #include "BKE_colortools.hh"
@ -23,6 +26,7 @@
#include "BKE_object.hh" #include "BKE_object.hh"
#include "BLI_color.hh" #include "BLI_color.hh"
#include "BLI_function_ref.hh"
#include "BLI_listbase.h" #include "BLI_listbase.h"
#include "BLI_math_vector_types.hh" #include "BLI_math_vector_types.hh"
#include "BLI_string.h" #include "BLI_string.h"
@ -31,16 +35,86 @@
#include "BLT_translation.hh" #include "BLT_translation.hh"
#include "DNA_anim_types.h"
#include "DNA_gpencil_legacy_types.h" #include "DNA_gpencil_legacy_types.h"
#include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_modifier_types.h"
#include "DNA_grease_pencil_types.h" #include "DNA_grease_pencil_types.h"
#include "DNA_meshdata_types.h" #include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h" #include "DNA_modifier_types.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh" #include "DEG_depsgraph_build.hh"
namespace blender::bke::greasepencil::convert { namespace blender::bke::greasepencil::convert {
/* -------------------------------------------------------------------- */
/** \name Animation conversion helpers.
*
* These utilities will call given callback over all relevant F-curves
* (also includes drivers, and actions linked through the NLA).
* \{ */
static bool legacy_fcurves_process(ListBase &fcurves,
blender::FunctionRef<bool(FCurve *fcurve)> callback)
{
bool is_changed = false;
LISTBASE_FOREACH (FCurve *, fcurve, &fcurves) {
const bool local_is_changed = callback(fcurve);
is_changed = is_changed || local_is_changed;
}
return is_changed;
}
static bool legacy_nla_strip_process(NlaStrip &nla_strip,
blender::FunctionRef<bool(FCurve *fcurve)> callback)
{
bool is_changed = false;
if (nla_strip.act) {
if (legacy_fcurves_process(nla_strip.act->curves, callback)) {
DEG_id_tag_update(&nla_strip.act->id, ID_RECALC_ANIMATION);
is_changed = true;
}
}
LISTBASE_FOREACH (NlaStrip *, nla_strip_children, &nla_strip.strips) {
const bool local_is_changed = legacy_nla_strip_process(*nla_strip_children, callback);
is_changed = is_changed || local_is_changed;
}
return is_changed;
}
static bool legacy_animation_process(AnimData &anim_data,
blender::FunctionRef<bool(FCurve *fcurve)> callback)
{
bool is_changed = false;
if (anim_data.action) {
if (legacy_fcurves_process(anim_data.action->curves, callback)) {
DEG_id_tag_update(&anim_data.action->id, ID_RECALC_ANIMATION);
is_changed = true;
}
}
if (anim_data.tmpact) {
if (legacy_fcurves_process(anim_data.tmpact->curves, callback)) {
DEG_id_tag_update(&anim_data.tmpact->id, ID_RECALC_ANIMATION);
is_changed = true;
}
}
{
const bool local_is_changed = legacy_fcurves_process(anim_data.drivers, callback);
is_changed = is_changed || local_is_changed;
}
LISTBASE_FOREACH (NlaTrack *, nla_track, &anim_data.nla_tracks) {
LISTBASE_FOREACH (NlaStrip *, nla_strip, &nla_track->strips) {
const bool local_is_changed = legacy_nla_strip_process(*nla_strip, callback);
is_changed = is_changed || local_is_changed;
}
}
return is_changed;
}
/* \} */
/** /**
* Find vertex groups that have assigned vertices in this drawing. * Find vertex groups that have assigned vertices in this drawing.
* Returns: * Returns:
@ -384,6 +458,18 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
/* TODO: Update drawing user counts. */ /* TODO: Update drawing user counts. */
} }
/* Second loop, to write to layer attributes after all layers were created. */
MutableAttributeAccessor layer_attributes = grease_pencil.attributes_for_write();
SpanAttributeWriter<int> layer_passes = layer_attributes.lookup_or_add_for_write_span<int>(
"pass_index", bke::AttrDomain::Layer);
layer_idx = 0;
LISTBASE_FOREACH_INDEX (bGPDlayer *, gpl, &gpd.layers, layer_idx) {
layer_passes.span[layer_idx] = int(gpl->pass_index);
}
layer_passes.finish();
/* Copy vertex group names and settings. */ /* Copy vertex group names and settings. */
BKE_defgroup_copy_list(&grease_pencil.vertex_group_names, &gpd.vertex_group_names); BKE_defgroup_copy_list(&grease_pencil.vertex_group_names, &gpd.vertex_group_names);
grease_pencil.vertex_group_active_index = gpd.vertex_group_active_index; grease_pencil.vertex_group_active_index = gpd.vertex_group_active_index;
@ -618,6 +704,33 @@ static ModifierData &legacy_object_modifier_common(Object &object,
/* Attempt to copy UI state (panels) as best as possible. */ /* Attempt to copy UI state (panels) as best as possible. */
new_md.ui_expand_flag = legacy_md.ui_expand_flag; new_md.ui_expand_flag = legacy_md.ui_expand_flag;
/* Convert animation data if needed. */
AnimData *anim_data = BKE_animdata_from_id(&object.id);
if (anim_data) {
auto modifier_path_update = [&](FCurve *fcurve) -> bool {
/* NOTE: This logic will likely need to be re-used in other similar conditions for other
* areas, should be put into its own util then. */
if (!fcurve->rna_path) {
return false;
}
StringRefNull rna_path = fcurve->rna_path;
const std::string legacy_root_path = fmt::format("grease_pencil_modifiers[\"{}\"]",
legacy_md.name);
if (!rna_path.startswith(legacy_root_path)) {
return false;
}
const std::string new_rna_path = fmt::format(
"modifiers[\"{}\"]{}", new_md.name, rna_path.substr(int64_t(legacy_root_path.size())));
MEM_freeN(fcurve->rna_path);
fcurve->rna_path = BLI_strdupn(new_rna_path.c_str(), new_rna_path.size());
return true;
};
if (legacy_animation_process(*anim_data, modifier_path_update)) {
DEG_id_tag_update(&object.id, ID_RECALC_ANIMATION);
}
}
return new_md; return new_md;
} }

View File

@ -597,7 +597,7 @@ static bool main_namemap_validate_and_fix(Main *bmain, const bool do_fix)
return is_valid; return is_valid;
} }
/* Clear all existing namemaps. */ /* Clear all existing name-maps. */
BKE_main_namemap_clear(bmain); BKE_main_namemap_clear(bmain);
return is_valid; return is_valid;

View File

@ -902,6 +902,58 @@ class NodeTreeMainUpdater {
} }
} }
/* Find conflicts between on corresponding menu sockets on different group input nodes. */
const Span<bNode *> group_input_nodes = ntree.group_input_nodes();
for (const int interface_input_i : ntree.interface_inputs().index_range()) {
const bNodeTreeInterfaceSocket &interface_socket =
*ntree.interface_inputs()[interface_input_i];
if (interface_socket.socket_type != StringRef("NodeSocketMenu")) {
continue;
}
const RuntimeNodeEnumItems *found_enum_items = nullptr;
bool found_conflict = false;
for (bNode *input_node : group_input_nodes) {
const bNodeSocket &socket = input_node->output_socket(interface_input_i);
const auto &socket_value = *socket.default_value_typed<bNodeSocketValueMenu>();
if (socket_value.has_conflict()) {
found_conflict = true;
break;
}
if (found_enum_items == nullptr) {
found_enum_items = socket_value.enum_items;
}
else if (socket_value.enum_items != nullptr) {
if (found_enum_items != socket_value.enum_items) {
found_conflict = true;
break;
}
}
}
if (found_conflict) {
/* Make sure that all group input sockets know that there is a socket. */
for (bNode *input_node : group_input_nodes) {
bNodeSocket &socket = input_node->output_socket(interface_input_i);
auto &socket_value = *socket.default_value_typed<bNodeSocketValueMenu>();
if (socket_value.enum_items) {
socket_value.enum_items->remove_user_and_delete_if_last();
socket_value.enum_items = nullptr;
}
socket_value.runtime_flag |= NodeSocketValueMenuRuntimeFlag::NODE_MENU_ITEMS_CONFLICT;
}
}
else if (found_enum_items != nullptr) {
/* Make sure all corresponding menu sockets have the same menu reference. */
for (bNode *input_node : group_input_nodes) {
bNodeSocket &socket = input_node->output_socket(interface_input_i);
auto &socket_value = *socket.default_value_typed<bNodeSocketValueMenu>();
if (socket_value.enum_items == nullptr) {
found_enum_items->add_user();
socket_value.enum_items = found_enum_items;
}
}
}
}
/* Build list of new enum items for the node tree interface. */ /* Build list of new enum items for the node tree interface. */
Vector<bNodeSocketValueMenu> interface_enum_items(ntree.interface_inputs().size(), {0}); Vector<bNodeSocketValueMenu> interface_enum_items(ntree.interface_inputs().size(), {0});
for (const bNode *group_input_node : ntree.group_input_nodes()) { for (const bNode *group_input_node : ntree.group_input_nodes()) {

View File

@ -278,6 +278,82 @@ bUserExtensionRepo *BKE_preferences_extension_repo_find_by_module(const UserDef
BLI_findstring(&userdef->extension_repos, module, offsetof(bUserExtensionRepo, module))); BLI_findstring(&userdef->extension_repos, module, offsetof(bUserExtensionRepo, module)));
} }
bUserExtensionRepo *BKE_preferences_extension_repo_find_by_remote_path_prefix(
const UserDef *userdef, const char *path_full, const bool only_enabled)
{
const int path_full_len = strlen(path_full);
const int path_full_offset = BKE_preferences_extension_repo_remote_scheme_end(path_full);
LISTBASE_FOREACH (bUserExtensionRepo *, repo, &userdef->extension_repos) {
if (only_enabled && (repo->flag & USER_EXTENSION_REPO_FLAG_DISABLED)) {
continue;
}
/* Has a valid remote path to check. */
if ((repo->flag & USER_EXTENSION_REPO_FLAG_USE_REMOTE_PATH) == 0) {
continue;
}
if (repo->remote_path[0] == '\0') {
continue;
}
/* Set path variables which may be offset by the "scheme". */
const char *path_repo = repo->remote_path;
const char *path_test = path_full;
int path_test_len = path_full_len;
/* Allow paths beginning with both `http` & `https` to be considered equivalent.
* This is done by skipping the "scheme" prefix both have a scheme. */
if (path_full_offset) {
const int path_repo_offset = BKE_preferences_extension_repo_remote_scheme_end(path_repo);
if (path_repo_offset) {
path_repo += path_repo_offset;
path_test += path_full_offset;
path_test_len -= path_full_offset;
}
}
/* The length of the path without trailing slashes. */
int path_repo_len = strlen(path_repo);
while (path_repo_len && ELEM(path_repo[path_repo_len - 1], '/', '\\')) {
path_repo_len--;
}
if (path_test_len <= path_repo_len) {
continue;
}
if (memcmp(path_repo, path_test, path_repo_len) != 0) {
continue;
}
/* A delimiter must follow to ensure `path_test` doesn't reference a longer host-name.
* Will typically be a `/` or a `:`. */
if (!ELEM(path_test[path_repo_len], '/', '\\', ':', '&', '?')) {
continue;
}
return repo;
}
return nullptr;
}
int BKE_preferences_extension_repo_remote_scheme_end(const char *url)
{
/* Technically the "://" are not part of the scheme, so subtract 3 from the return value. */
const char *scheme_check[] = {
"http://",
"https://",
"file://",
};
for (int i = 0; i < ARRAY_SIZE(scheme_check); i++) {
const char *scheme = scheme_check[i];
int scheme_len = strlen(scheme);
if (strncmp(url, scheme, scheme_len) == 0) {
return scheme_len - 3;
}
}
return 0;
}
int BKE_preferences_extension_repo_get_index(const UserDef *userdef, int BKE_preferences_extension_repo_get_index(const UserDef *userdef,
const bUserExtensionRepo *repo) const bUserExtensionRepo *repo)
{ {

View File

@ -261,7 +261,7 @@ template<typename T> inline Vector<IndexRange> find_all_ranges(const Span<T> spa
int64_t length = (span.first() == value) ? 1 : 0; int64_t length = (span.first() == value) ? 1 : 0;
for (const int64_t i : span.index_range().drop_front(1)) { for (const int64_t i : span.index_range().drop_front(1)) {
if (span[i - 1] == value && span[i] != value) { if (span[i - 1] == value && span[i] != value) {
ranges.append(IndexRange(i - length, length)); ranges.append(IndexRange::from_end_size(i, length));
length = 0; length = 0;
} }
else if (span[i] == value) { else if (span[i] == value) {
@ -269,7 +269,7 @@ template<typename T> inline Vector<IndexRange> find_all_ranges(const Span<T> spa
} }
} }
if (length > 0) { if (length > 0) {
ranges.append(IndexRange(span.size() - length, length)); ranges.append(IndexRange::from_end_size(span.size(), length));
} }
return ranges; return ranges;
} }

View File

@ -236,7 +236,7 @@ class BitVector {
IndexRange index_range() const IndexRange index_range() const
{ {
return {0, size_in_bits_}; return IndexRange(size_in_bits_);
} }
/** /**
@ -283,7 +283,7 @@ class BitVector {
} }
size_in_bits_ = new_size_in_bits; size_in_bits_ = new_size_in_bits;
if (old_size_in_bits < new_size_in_bits) { if (old_size_in_bits < new_size_in_bits) {
MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits)) MutableBitSpan(data_, IndexRange::from_begin_end(old_size_in_bits, new_size_in_bits))
.set_all(value); .set_all(value);
} }
} }

View File

@ -116,7 +116,19 @@ class IndexMaskMemory : public LinearAllocator<> {
} }
}; };
using IndexMaskSegment = OffsetSpan<int64_t, int16_t>; /**
* A sequence of unique and ordered indices in one segment of an IndexMask. The segment as a whole
* has an `int64_t` index offset that is added to each referenced `int16_t` index.
*/
class IndexMaskSegment : public OffsetSpan<int64_t, int16_t> {
public:
using OffsetSpan<int64_t, int16_t>::OffsetSpan;
explicit IndexMaskSegment(const OffsetSpan<int64_t, int16_t> span);
IndexMaskSegment slice(const IndexRange &range) const;
IndexMaskSegment slice(const int64_t start, const int64_t size) const;
};
/** /**
* An #IndexMask is a sequence of unique and sorted indices (`BLI_unique_sorted_indices.hh`). * An #IndexMask is a sequence of unique and sorted indices (`BLI_unique_sorted_indices.hh`).
@ -536,6 +548,26 @@ inline bool operator==(const RawMaskIterator &a, const RawMaskIterator &b)
return !(a != b); return !(a != b);
} }
/* -------------------------------------------------------------------- */
/** \name #IndexMaskSegment Inline Methods
* \{ */
inline IndexMaskSegment::IndexMaskSegment(const OffsetSpan<int64_t, int16_t> span)
: OffsetSpan<int64_t, int16_t>(span)
{
}
inline IndexMaskSegment IndexMaskSegment::slice(const IndexRange &range) const
{
return IndexMaskSegment(static_cast<const OffsetSpan<int64_t, int16_t> *>(this)->slice(range));
}
inline IndexMaskSegment IndexMaskSegment::slice(const int64_t start, const int64_t size) const
{
return IndexMaskSegment(
static_cast<const OffsetSpan<int64_t, int16_t> *>(this)->slice(start, size));
}
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name #IndexMask Inline Methods /** \name #IndexMask Inline Methods
* \{ */ * \{ */
@ -612,8 +644,7 @@ inline IndexRange IndexMask::bounds() const
} }
const int64_t first = this->first(); const int64_t first = this->first();
const int64_t last = this->last(); const int64_t last = this->last();
const int64_t range = last - first + 1; return IndexRange::from_begin_end_inclusive(first, last);
return IndexRange(first, range);
} }
inline int64_t IndexMask::first() const inline int64_t IndexMask::first() const

View File

@ -60,12 +60,37 @@ class IndexRange {
BLI_assert(size >= 0); BLI_assert(size >= 0);
} }
constexpr IndexRange(int64_t start, int64_t size) : start_(start), size_(size) constexpr IndexRange(const int64_t start, const int64_t size) : start_(start), size_(size)
{ {
BLI_assert(start >= 0); BLI_assert(start >= 0);
BLI_assert(size >= 0); BLI_assert(size >= 0);
} }
constexpr static IndexRange from_begin_size(const int64_t begin, const int64_t size)
{
return IndexRange(begin, size);
}
constexpr static IndexRange from_begin_end(const int64_t begin, const int64_t end)
{
return IndexRange(begin, end - begin);
}
constexpr static IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
{
return IndexRange(begin, last - begin + 1);
}
constexpr static IndexRange from_end_size(const int64_t end, const int64_t size)
{
return IndexRange(end - size, size);
}
constexpr static IndexRange from_single(const int64_t index)
{
return IndexRange(index, 1);
}
class Iterator : public iterator::RandomAccessIteratorMixin<Iterator> { class Iterator : public iterator::RandomAccessIteratorMixin<Iterator> {
public: public:
using value_type = int64_t; using value_type = int64_t;

View File

@ -66,16 +66,14 @@ template<typename T> class OffsetIndices {
BLI_assert(index < offsets_.size() - 1); BLI_assert(index < offsets_.size() - 1);
const int64_t begin = offsets_[index]; const int64_t begin = offsets_[index];
const int64_t end = offsets_[index + 1]; const int64_t end = offsets_[index + 1];
const int64_t size = end - begin; return IndexRange::from_begin_end(begin, end);
return IndexRange(begin, size);
} }
IndexRange operator[](const IndexRange indices) const IndexRange operator[](const IndexRange indices) const
{ {
const int64_t begin = offsets_[indices.start()]; const int64_t begin = offsets_[indices.start()];
const int64_t end = offsets_[indices.one_after_last()]; const int64_t end = offsets_[indices.one_after_last()];
const int64_t size = end - begin; return IndexRange::from_begin_end(begin, end);
return IndexRange(begin, size);
} }
/** /**

View File

@ -101,7 +101,7 @@ inline IndexRange align_sub_range(const IndexRange unaligned_range,
const int64_t aligned_end = unaligned_end == global_end ? const int64_t aligned_end = unaligned_end == global_end ?
unaligned_end : unaligned_end :
std::max(global_begin, unaligned_end & alignment_mask); std::max(global_begin, unaligned_end & alignment_mask);
const IndexRange aligned_range{aligned_begin, aligned_end - aligned_begin}; const IndexRange aligned_range = IndexRange::from_begin_end(aligned_begin, aligned_end);
return aligned_range; return aligned_range;
} }

View File

@ -480,7 +480,18 @@ int BLI_rename(const char *from, const char *to)
#elif defined(__GLIBC_PREREQ) #elif defined(__GLIBC_PREREQ)
# if __GLIBC_PREREQ(2, 28) # if __GLIBC_PREREQ(2, 28)
/* Most common Linux cases. */ /* Most common Linux cases. */
return renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE); int ret = renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE);
if (ret < 0 && errno == EINVAL) {
/* Most likely a filesystem that doesn't support RENAME_NOREPLACE.
* (For example NFS, Samba, exFAT, NTFS, etc)
* Retry with a non atomic operation.
*/
if (BLI_exists(to)) {
return 1;
}
return rename(from, to);
}
return ret;
# endif # endif
#else #else
/* At least all BSD's currently. */ /* At least all BSD's currently. */

View File

@ -286,7 +286,7 @@ int BLI_hash_md5_stream(FILE *stream, void *resblock)
len[1] = 0; len[1] = 0;
/* Iterate over full file contents. */ /* Iterate over full file contents. */
while (1) { while (true) {
/* We read the file in blocks of BLOCKSIZE bytes. /* We read the file in blocks of BLOCKSIZE bytes.
* One call of the computation function processes the whole buffer * One call of the computation function processes the whole buffer
* so that with the next round of the loop another block can be read. * so that with the next round of the loop another block can be read.

View File

@ -235,7 +235,7 @@ static void consolidate_segments(Vector<IndexMaskSegment, 16> &segments,
return; return;
} }
/* Join multiple ranges together into a bigger range. */ /* Join multiple ranges together into a bigger range. */
const IndexRange range{group_first, group_last + 1 - group_first}; const IndexRange range = IndexRange::from_begin_end_inclusive(group_first, group_last);
segments[group_start_segment_i] = IndexMaskSegment(range[0], segments[group_start_segment_i] = IndexMaskSegment(range[0],
static_indices.take_front(range.size())); static_indices.take_front(range.size()));
for (int64_t i = group_start_segment_i + 1; i <= last_segment_i; i++) { for (int64_t i = group_start_segment_i + 1; i <= last_segment_i; i++) {
@ -500,16 +500,12 @@ IndexMask IndexMask::complement(const IndexRange universe, IndexMaskMemory &memo
if (first_in_range) { if (first_in_range) {
/* This mask is a range that contains the start of the universe. /* This mask is a range that contains the start of the universe.
* The complement is a range that contains the end of the universe. */ * The complement is a range that contains the end of the universe. */
const int64_t complement_start = this_range->one_after_last(); return IndexRange::from_begin_end(this_range->one_after_last(), universe.one_after_last());
const int64_t complement_size = universe.one_after_last() - complement_start;
return IndexRange(complement_start, complement_size);
} }
if (last_in_range) { if (last_in_range) {
/* This mask is a range that contains the end of the universe. /* This mask is a range that contains the end of the universe.
* The complement is a range that contains the start of the universe. */ * The complement is a range that contains the start of the universe. */
const int64_t complement_start = universe.first(); return IndexRange::from_begin_end(universe.first(), this_range->first());
const int64_t complement_size = this_range->first() - complement_start;
return IndexRange(complement_start, complement_size);
} }
} }

View File

@ -24,7 +24,7 @@ TEST(index_mask, IndicesToMask)
EXPECT_EQ(mask.first(), 5); EXPECT_EQ(mask.first(), 5);
EXPECT_EQ(mask.last(), 101000); EXPECT_EQ(mask.last(), 101000);
EXPECT_EQ(mask.min_array_size(), 101001); EXPECT_EQ(mask.min_array_size(), 101001);
EXPECT_EQ(mask.bounds(), IndexRange(5, 101001 - 5)); EXPECT_EQ(mask.bounds(), IndexRange::from_begin_end_inclusive(5, 101000));
} }
TEST(index_mask, FromBits) TEST(index_mask, FromBits)

View File

@ -23,6 +23,86 @@ TEST(index_range, DefaultConstructor)
EXPECT_EQ(vector.size(), 0); EXPECT_EQ(vector.size(), 0);
} }
TEST(index_range, FromBeginSize)
{
{
const IndexRange range = IndexRange::from_begin_size(0, 0);
EXPECT_TRUE(range.is_empty());
}
{
const IndexRange range = IndexRange::from_begin_size(0, 10);
EXPECT_EQ(range.size(), 10);
EXPECT_EQ(range.first(), 0);
}
{
const IndexRange range = IndexRange::from_begin_size(4, 10);
EXPECT_EQ(range.size(), 10);
EXPECT_EQ(range.first(), 4);
EXPECT_EQ(range.last(), 13);
}
}
TEST(index_range, FromBeginEnd)
{
{
const IndexRange range = IndexRange::from_begin_end(0, 0);
EXPECT_TRUE(range.is_empty());
}
{
const IndexRange range = IndexRange::from_begin_end(0, 10);
EXPECT_EQ(range.size(), 10);
EXPECT_EQ(range.first(), 0);
}
{
const IndexRange range = IndexRange::from_begin_end(4, 10);
EXPECT_EQ(range.size(), 6);
EXPECT_EQ(range.first(), 4);
EXPECT_EQ(range.last(), 9);
}
}
TEST(index_range, FromBeginEndInclusive)
{
{
const IndexRange range = IndexRange::from_begin_end_inclusive(0, 0);
EXPECT_EQ(range.size(), 1);
EXPECT_EQ(range.first(), 0);
}
{
const IndexRange range = IndexRange::from_begin_end_inclusive(100, 200);
EXPECT_EQ(range.size(), 101);
EXPECT_EQ(range.first(), 100);
EXPECT_EQ(range.last(), 200);
}
}
TEST(index_range, FromEndSize)
{
{
const IndexRange range = IndexRange::from_end_size(0, 0);
EXPECT_TRUE(range.is_empty());
}
{
const IndexRange range = IndexRange::from_end_size(100, 20);
EXPECT_EQ(range.first(), 80);
EXPECT_EQ(range.last(), 99);
}
}
TEST(index_range, FromSingle)
{
{
const IndexRange range = IndexRange::from_single(0);
EXPECT_EQ(range.size(), 1);
EXPECT_EQ(range.first(), 0);
}
{
const IndexRange range = IndexRange::from_single(100);
EXPECT_EQ(range.size(), 1);
EXPECT_EQ(range.first(), 100);
}
}
TEST(index_range, SingleElementRange) TEST(index_range, SingleElementRange)
{ {
IndexRange range(4, 1); IndexRange range(4, 1);

View File

@ -9,7 +9,7 @@ class BlendfileLoadingTest : public BlendfileLoadingBaseTest {};
TEST_F(BlendfileLoadingTest, CanaryTest) TEST_F(BlendfileLoadingTest, CanaryTest)
{ {
/* Load the smallest blend file we have in the SVN lib/tests directory. */ /* Load the smallest blend file we have in the tests/data directory. */
if (!blendfile_load("modifier_stack" SEP_STR "array_test.blend")) { if (!blendfile_load("modifier_stack" SEP_STR "array_test.blend")) {
return; return;
} }

View File

@ -25,10 +25,10 @@ class BlendfileLoadingBaseTest : public testing::Test {
/* Frees the depsgraph & blendfile. */ /* Frees the depsgraph & blendfile. */
virtual void TearDown(); virtual void TearDown();
/* Loads a blend file from the lib/tests directory from SVN. /* Loads a blend file from the tests/data directory from SVN.
* Returns 'ok' flag (true=good, false=bad) and sets this->bfile. * Returns 'ok' flag (true=good, false=bad) and sets this->bfile.
* Fails the test if the file cannot be loaded (still returns though). * Fails the test if the file cannot be loaded (still returns though).
* Requires the CLI argument --test-asset-dir to point to ../../lib/tests. * Requires the CLI argument --test-asset-dir to point to ../tests/data.
* *
* WARNING: only files saved with Blender 2.80+ can be loaded. Since Blender * WARNING: only files saved with Blender 2.80+ can be loaded. Since Blender
* is only partially initialized (most importantly, without window manager), * is only partially initialized (most importantly, without window manager),

View File

@ -32,7 +32,7 @@ void HueSaturationValueCorrectOperation::execute_pixel_sampled(float output[4],
input_program_->read_sampled(hsv, x, y, sampler); input_program_->read_sampled(hsv, x, y, sampler);
/* We parametrize the curve using the hue value. */ /* We parameterize the curve using the hue value. */
const float parameter = hsv[0]; const float parameter = hsv[0];
/* adjust hue, scaling returned default 0.5 up to 1 */ /* adjust hue, scaling returned default 0.5 up to 1 */
@ -70,7 +70,7 @@ void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuff
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
copy_v4_v4(hsv, it.in(0)); copy_v4_v4(hsv, it.in(0));
/* We parametrize the curve using the hue value. */ /* We parameterize the curve using the hue value. */
const float parameter = hsv[0]; const float parameter = hsv[0];
/* Adjust hue, scaling returned default 0.5 up to 1. */ /* Adjust hue, scaling returned default 0.5 up to 1. */

View File

@ -27,7 +27,7 @@ float roughness_from_relative_mip(float prev_mip_roughness, float curr_mip_rough
float m_prev = pow(prev_mip_roughness, exponent); float m_prev = pow(prev_mip_roughness, exponent);
float m_curr = pow(curr_mip_roughness, exponent); float m_curr = pow(curr_mip_roughness, exponent);
/* Given that spherical gaussians are very close to regular gaussian in 1D, /* Given that spherical gaussians are very close to regular gaussian in 1D,
* we reuse the same rule for successive convolution (i.e: G`(x,a) X G(x,b) = G(x,a+b)`). * we reuse the same rule for successive convolution (i.e: `G(x,a) X G(x,b) = G(x,a+b)`).
* While this isn't technically correct, this still works quite well in practice. */ * While this isn't technically correct, this still works quite well in practice. */
float m_target = m_curr - m_prev; float m_target = m_curr - m_prev;
/* From GGX roughness input to linear roughness. */ /* From GGX roughness input to linear roughness. */

View File

@ -93,7 +93,7 @@ static void motion_path_get_frame_range_to_draw(bAnimVizSettings *avs,
} }
else { else {
start = avs->path_sf; start = avs->path_sf;
end = avs->path_ef; end = avs->path_ef + 1;
} }
if (start > end) { if (start > end) {

View File

@ -3044,9 +3044,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) { if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
float3 r1, r2; float3 r1, r2;
float3 p_cage_dummy; float3 p_cage_dummy;
/* Can't just interpolate between ends of kfe because /* Can't just interpolate between ends of `kfe` because
* that doesn't work with perspective transformation. * that doesn't work with perspective transformation.
* Need to find 3d intersection of ray through sint. */ * Need to find 3d intersection of ray through `p_cage_ss`. */
knife_input_ray_segment(kcd, p_cage_ss, r1, r2); knife_input_ray_segment(kcd, p_cage_ss, r1, r2);
isect_kind = isect_line_line_v3( isect_kind = isect_line_line_v3(

View File

@ -821,26 +821,28 @@ static void renamebutton_cb(bContext *C, void * /*arg1*/, char *oldname)
BLI_path_join(newname, sizeof(newname), params->dir, filename); BLI_path_join(newname, sizeof(newname), params->dir, filename);
if (!STREQ(orgname, newname)) { if (!STREQ(orgname, newname)) {
if (!BLI_exists(newname)) { errno = 0;
errno = 0; if ((BLI_rename(orgname, newname) != 0) || !BLI_exists(newname)) {
if ((BLI_rename(orgname, newname) != 0) || !BLI_exists(newname)) { WM_reportf(RPT_ERROR, "Could not rename: %s", errno ? strerror(errno) : "unknown error");
WM_reportf(RPT_ERROR, "Could not rename: %s", errno ? strerror(errno) : "unknown error"); WM_report_banner_show(wm, win);
WM_report_banner_show(wm, win);
}
else {
/* If rename is successful, scroll to newly renamed entry. */
STRNCPY(params->renamefile, filename);
file_params_invoke_rename_postscroll(wm, win, sfile);
}
/* to make sure we show what is on disk */
ED_fileselect_clear(wm, sfile);
}
else {
/* Renaming failed, reset the name for further renaming handling. */ /* Renaming failed, reset the name for further renaming handling. */
STRNCPY(params->renamefile, oldname); STRNCPY(params->renamefile, oldname);
} }
else {
/* If rename is successful, set renamefile to newly renamed entry.
* This is used later to select and scroll to the file.
*/
STRNCPY(params->renamefile, filename);
}
/* Ensure we select and scroll to the renamed file.
* This is done even if the rename fails as we want to make sure that the file we tried to
* rename is still selected and in view. (it can move if something added files/folders to the
* directory while we were renaming.
*/
file_params_invoke_rename_postscroll(wm, win, sfile);
/* to make sure we show what is on disk */
ED_fileselect_clear(wm, sfile);
ED_region_tag_redraw(region); ED_region_tag_redraw(region);
} }
} }

View File

@ -786,11 +786,16 @@ static bool drop_extension_url_poll(bContext * /*C*/, wmDrag *drag, const wmEven
return false; return false;
} }
/* NOTE(@ideasman42): it should be possible to drag a URL into the text editor or Python console.
* In the future we may support dragging images into Blender by URL, so treating any single-line
* URL as an extension could back-fire. Avoid problems in the future by limiting the text which
* is accepted as an extension to ZIP's or URL's that reference known repositories. */
const std::string &str = WM_drag_get_string(drag); const std::string &str = WM_drag_get_string(drag);
/* Only URL formatted text. */ /* Only URL formatted text. */
const char *cstr = str.c_str(); const char *cstr = str.c_str();
if (!(STRPREFIX(cstr, "http://") || STRPREFIX(cstr, "https://") || STRPREFIX(cstr, "file://"))) { if (BKE_preferences_extension_repo_remote_scheme_end(cstr) == 0) {
return false; return false;
} }
@ -798,8 +803,13 @@ static bool drop_extension_url_poll(bContext * /*C*/, wmDrag *drag, const wmEven
if (str.find('\n') != std::string::npos) { if (str.find('\n') != std::string::npos) {
return false; return false;
} }
const char *cstr_ext = BLI_path_extension(cstr); const char *cstr_ext = BLI_path_extension(cstr);
if (!(cstr_ext && STRCASEEQ(cstr_ext, ".zip"))) { /* Check the URL has a `.zip` suffix OR has a known repository as a prefix.
* This is needed to support redirects which don't contain an extension. */
if (!(cstr_ext && STRCASEEQ(cstr_ext, ".zip")) &&
!(BKE_preferences_extension_repo_find_by_remote_path_prefix(&U, cstr, true)))
{
return false; return false;
} }

View File

@ -140,6 +140,9 @@ void VKVertexBuffer::upload_data_direct(const VKBuffer &host_buffer)
{ {
device_format_ensure(); device_format_ensure();
if (vertex_format_converter.needs_conversion()) { if (vertex_format_converter.needs_conversion()) {
if (G.debug & G_DEBUG_GPU) {
std::cout << "PERFORMANCE: Vertex buffer requires conversion.\n";
}
vertex_format_converter.convert(host_buffer.mapped_memory_get(), data, vertex_len); vertex_format_converter.convert(host_buffer.mapped_memory_get(), data, vertex_len);
host_buffer.flush(); host_buffer.flush();
} }

View File

@ -333,7 +333,6 @@ void IMB_close_anim(ImBufAnim *anim);
void IMB_close_anim_proxies(ImBufAnim *anim); void IMB_close_anim_proxies(ImBufAnim *anim);
bool IMB_anim_can_produce_frames(const ImBufAnim *anim); bool IMB_anim_can_produce_frames(const ImBufAnim *anim);
int ismovie(const char *filepath);
int IMB_anim_get_image_width(ImBufAnim *anim); int IMB_anim_get_image_width(ImBufAnim *anim);
int IMB_anim_get_image_height(ImBufAnim *anim); int IMB_anim_get_image_height(ImBufAnim *anim);
bool IMB_get_gop_decode_time(ImBufAnim *anim); bool IMB_get_gop_decode_time(ImBufAnim *anim);
@ -397,12 +396,12 @@ bool IMB_ispic_type_matches(const char *filepath, int filetype);
int IMB_ispic_type_from_memory(const unsigned char *buf, size_t buf_size); int IMB_ispic_type_from_memory(const unsigned char *buf, size_t buf_size);
int IMB_ispic_type(const char *filepath); int IMB_ispic_type(const char *filepath);
enum class ImbAnimType { NotAnim, Sequence, Movie, Ffmpeg }; /**
* Test if the file is a video file (known format, has a video stream and
* supported video codec).
*/
bool IMB_isanim(const char *filepath); bool IMB_isanim(const char *filepath);
ImbAnimType imb_get_anim_type(const char *filepath);
/** /**
* Test if color-space conversions of pixels in buffer need to take into account alpha. * Test if color-space conversions of pixels in buffer need to take into account alpha.
*/ */

View File

@ -8,36 +8,26 @@
#pragma once #pragma once
#include <ctype.h> #include <cstdint>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#ifdef _WIN32 #include "IMB_imbuf_enums.h"
# include <io.h>
#else
# include <dirent.h>
#endif
#include "imbuf.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
extern "C" { struct AVFormatContext;
# include <libavcodec/avcodec.h> struct AVCodecContext;
# include <libavformat/avformat.h> struct AVCodec;
# include <libswscale/swscale.h> struct AVFrame;
} struct AVPacket;
struct SwsContext;
#endif #endif
struct IDProperty; struct IDProperty;
struct ImBufAnimIndex; struct ImBufAnimIndex;
struct ImBufAnim { struct ImBufAnim {
enum class State { Uninitialized, Failed, Valid };
int ib_flags; int ib_flags;
ImbAnimType curtype; State state;
int cur_position; /* index 0 = 1e, 1 = 2e, enz. */ int cur_position; /* index 0 = 1e, 1 = 2e, enz. */
int duration_in_frames; int duration_in_frames;
int frs_sec; int frs_sec;
@ -47,16 +37,7 @@ struct ImBufAnim {
/* for number */ /* for number */
char filepath[1024]; char filepath[1024];
/* for sequence */
char filepath_first[1024];
/* movie */
void *movie;
void *track;
void *params;
int orientation;
size_t framesize;
int interlacing;
int streamindex; int streamindex;
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
@ -65,7 +46,7 @@ struct ImBufAnim {
const AVCodec *pCodec; const AVCodec *pCodec;
AVFrame *pFrameRGB; AVFrame *pFrameRGB;
AVFrame *pFrameDeinterlaced; AVFrame *pFrameDeinterlaced;
struct SwsContext *img_convert_ctx; SwsContext *img_convert_ctx;
int videoStream; int videoStream;
AVFrame *pFrame; AVFrame *pFrame;
@ -85,11 +66,11 @@ struct ImBufAnim {
int proxies_tried; int proxies_tried;
int indices_tried; int indices_tried;
struct ImBufAnim *proxy_anim[IMB_PROXY_MAX_SLOT]; ImBufAnim *proxy_anim[IMB_PROXY_MAX_SLOT];
struct ImBufAnimIndex *curr_idx[IMB_TC_MAX_SLOT]; ImBufAnimIndex *curr_idx[IMB_TC_MAX_SLOT];
char colorspace[64]; char colorspace[64];
char suffix[64]; /* MAX_NAME - multiview */ char suffix[64]; /* MAX_NAME - multiview */
struct IDProperty *metadata; IDProperty *metadata;
}; };

View File

@ -52,25 +52,6 @@ extern "C" {
#endif /* WITH_FFMPEG */ #endif /* WITH_FFMPEG */
int ismovie(const char * /*filepath*/)
{
return 0;
}
/* never called, just keep the linker happy */
static int startmovie(ImBufAnim * /*anim*/)
{
return 1;
}
static ImBuf *movie_fetchibuf(ImBufAnim * /*anim*/, int /*position*/)
{
return nullptr;
}
static void free_anim_movie(ImBufAnim * /*anim*/)
{
/* pass */
}
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
static void free_anim_ffmpeg(ImBufAnim *anim); static void free_anim_ffmpeg(ImBufAnim *anim);
#endif #endif
@ -82,8 +63,6 @@ void IMB_free_anim(ImBufAnim *anim)
return; return;
} }
free_anim_movie(anim);
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
free_anim_ffmpeg(anim); free_anim_ffmpeg(anim);
#endif #endif
@ -113,34 +92,23 @@ void IMB_close_anim_proxies(ImBufAnim *anim)
IDProperty *IMB_anim_load_metadata(ImBufAnim *anim) IDProperty *IMB_anim_load_metadata(ImBufAnim *anim)
{ {
switch (anim->curtype) { if (anim->state == ImBufAnim::State::Valid) {
case ImbAnimType::Ffmpeg: {
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
AVDictionaryEntry *entry = nullptr; BLI_assert(anim->pFormatCtx != nullptr);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n");
BLI_assert(anim->pFormatCtx != nullptr); AVDictionaryEntry *entry = nullptr;
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n"); while (true) {
entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX);
while (true) { if (entry == nullptr) {
entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX); break;
if (entry == nullptr) {
break;
}
/* Delay creation of the property group until there is actual metadata to put in there. */
IMB_metadata_ensure(&anim->metadata);
IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
} }
#endif
break; /* Delay creation of the property group until there is actual metadata to put in there. */
IMB_metadata_ensure(&anim->metadata);
IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
} }
case ImbAnimType::Sequence: #endif
case ImbAnimType::Movie:
/* TODO */
break;
case ImbAnimType::NotAnim:
default:
break;
} }
return anim->metadata; return anim->metadata;
} }
@ -376,8 +344,6 @@ static int startffmpeg(ImBufAnim *anim)
* starts. */ * starts. */
anim->start_offset = video_start; anim->start_offset = video_start;
anim->params = nullptr;
anim->x = pCodecCtx->width; anim->x = pCodecCtx->width;
anim->y = pCodecCtx->height; anim->y = pCodecCtx->height;
@ -386,10 +352,6 @@ static int startffmpeg(ImBufAnim *anim)
anim->pCodec = pCodec; anim->pCodec = pCodec;
anim->videoStream = video_stream_index; anim->videoStream = video_stream_index;
anim->interlacing = 0;
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
anim->cur_position = 0; anim->cur_position = 0;
anim->cur_pts = -1; anim->cur_pts = -1;
anim->cur_key_frame_pts = -1; anim->cur_key_frame_pts = -1;
@ -1234,48 +1196,21 @@ static void free_anim_ffmpeg(ImBufAnim *anim)
*/ */
static bool anim_getnew(ImBufAnim *anim) static bool anim_getnew(ImBufAnim *anim)
{ {
BLI_assert(anim->curtype == ImbAnimType::NotAnim);
if (anim == nullptr) { if (anim == nullptr) {
/* Nothing to initialize. */ /* Nothing to initialize. */
return false; return false;
} }
free_anim_movie(anim); BLI_assert(anim->state == ImBufAnim::State::Uninitialized);
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
free_anim_ffmpeg(anim); free_anim_ffmpeg(anim);
#endif if (startffmpeg(anim)) {
anim->state = ImBufAnim::State::Failed;
anim->curtype = imb_get_anim_type(anim->filepath); return false;
switch (anim->curtype) {
case ImbAnimType::Sequence: {
ImBuf *ibuf = IMB_loadiffname(anim->filepath, anim->ib_flags, anim->colorspace);
if (ibuf) {
STRNCPY(anim->filepath_first, anim->filepath);
anim->duration_in_frames = 1;
IMB_freeImBuf(ibuf);
}
else {
return false;
}
break;
}
case ImbAnimType::Movie:
if (startmovie(anim)) {
return false;
}
break;
#ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg:
if (startffmpeg(anim)) {
return false;
}
break;
#endif
default:
break;
} }
#endif
anim->state = ImBufAnim::State::Valid;
return true; return true;
} }
@ -1300,7 +1235,7 @@ ImBuf *IMB_anim_previewframe(ImBufAnim *anim)
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value); IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value);
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
if (anim->pFormatCtx && anim->curtype == ImbAnimType::Ffmpeg) { if (anim->pFormatCtx) {
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, nullptr); AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, nullptr);
if (frame_rate.num != 0) { if (frame_rate.num != 0) {
@ -1323,15 +1258,12 @@ ImBuf *IMB_anim_absolute(ImBufAnim *anim,
IMB_Proxy_Size preview_size) IMB_Proxy_Size preview_size)
{ {
ImBuf *ibuf = nullptr; ImBuf *ibuf = nullptr;
int filter_y;
if (anim == nullptr) { if (anim == nullptr) {
return nullptr; return nullptr;
} }
filter_y = (anim->ib_flags & IB_animdeinterlace);
if (preview_size == IMB_PROXY_NONE) { if (preview_size == IMB_PROXY_NONE) {
if (anim->curtype == ImbAnimType::NotAnim) { if (anim->state == ImBufAnim::State::Uninitialized) {
if (!anim_getnew(anim)) { if (!anim_getnew(anim)) {
return nullptr; return nullptr;
} }
@ -1354,45 +1286,16 @@ ImBuf *IMB_anim_absolute(ImBufAnim *anim,
} }
} }
switch (anim->curtype) {
case ImbAnimType::Sequence: {
constexpr size_t filepath_size = BOUNDED_ARRAY_TYPE_SIZE<decltype(anim->filepath_first)>();
char head[filepath_size], tail[filepath_size];
ushort digits;
const int pic = BLI_path_sequence_decode(
anim->filepath_first, head, sizeof(head), tail, sizeof(tail), &digits) +
position;
BLI_path_sequence_encode(anim->filepath, sizeof(anim->filepath), head, tail, digits, pic);
ibuf = IMB_loadiffname(anim->filepath, IB_rect, anim->colorspace);
if (ibuf) {
anim->cur_position = position;
}
break;
}
case ImbAnimType::Movie:
ibuf = movie_fetchibuf(anim, position);
if (ibuf) {
anim->cur_position = position;
IMB_convert_rgba_to_abgr(ibuf);
}
break;
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg: if (anim->state == ImBufAnim::State::Valid) {
ibuf = ffmpeg_fetchibuf(anim, position, tc); ibuf = ffmpeg_fetchibuf(anim, position, tc);
if (ibuf) { if (ibuf) {
anim->cur_position = position; anim->cur_position = position;
} }
filter_y = 0; /* done internally */
break;
#endif
default:
break;
} }
#endif
if (ibuf) { if (ibuf) {
if (filter_y) {
IMB_filtery(ibuf);
}
SNPRINTF(ibuf->filepath, "%s.%04d", anim->filepath, anim->cur_position + 1); SNPRINTF(ibuf->filepath, "%s.%04d", anim->filepath, anim->cur_position + 1);
} }
return ibuf; return ibuf;

View File

@ -458,9 +458,7 @@ static void get_tc_filepath(ImBufAnim *anim, IMB_Timecode_Type tc, char *filepat
* - common rebuilder structures * - common rebuilder structures
* ---------------------------------------------------------------------- */ * ---------------------------------------------------------------------- */
struct IndexBuildContext { struct IndexBuildContext {};
ImbAnimType anim_type;
};
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
* - ffmpeg rebuilder * - ffmpeg rebuilder
@ -786,8 +784,7 @@ static void free_proxy_output_ffmpeg(proxy_output_ctx *ctx, int rollback)
MEM_freeN(ctx); MEM_freeN(ctx);
} }
struct FFmpegIndexBuilderContext { struct FFmpegIndexBuilderContext : public IndexBuildContext {
int anim_type;
AVFormatContext *iFormatCtx; AVFormatContext *iFormatCtx;
AVCodecContext *iCodecCtx; AVCodecContext *iCodecCtx;
@ -1248,7 +1245,6 @@ IndexBuildContext *IMB_anim_index_rebuild_context(ImBufAnim *anim,
GSet *file_list, GSet *file_list,
bool build_only_on_bad_performance) bool build_only_on_bad_performance)
{ {
IndexBuildContext *context = nullptr;
int proxy_sizes_to_build = proxy_sizes_in_use; int proxy_sizes_to_build = proxy_sizes_in_use;
int i; int i;
@ -1297,24 +1293,16 @@ IndexBuildContext *IMB_anim_index_rebuild_context(ImBufAnim *anim,
return nullptr; return nullptr;
} }
switch (anim->curtype) { IndexBuildContext *context = nullptr;
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg: if (anim->state == ImBufAnim::State::Valid) {
context = index_ffmpeg_create_context( context = index_ffmpeg_create_context(
anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance); anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance);
break; }
#else #else
UNUSED_VARS(build_only_on_bad_performance); UNUSED_VARS(build_only_on_bad_performance);
#endif #endif
default:
break;
}
if (context) {
context->anim_type = anim->curtype;
}
return context; return context;
UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality); UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality);
@ -1328,35 +1316,25 @@ void IMB_anim_index_rebuild(IndexBuildContext *context,
/* NOLINTNEXTLINE: readability-non-const-parameter. */ /* NOLINTNEXTLINE: readability-non-const-parameter. */
float *progress) float *progress)
{ {
switch (context->anim_type) {
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg: if (context != nullptr) {
if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) { if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) {
index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
} }
break;
#endif
default:
break;
} }
#endif
UNUSED_VARS(stop, do_update, progress); UNUSED_VARS(context, stop, do_update, progress);
} }
void IMB_anim_index_rebuild_finish(IndexBuildContext *context, const bool stop) void IMB_anim_index_rebuild_finish(IndexBuildContext *context, const bool stop)
{ {
switch (context->anim_type) {
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
case ImbAnimType::Ffmpeg: if (context != nullptr) {
index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop); index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop);
break;
#endif
default:
break;
} }
#endif
/* static defined at top of the file */ /* static defined at top of the file */
UNUSED_VARS(stop, proxy_sizes); UNUSED_VARS(context, stop, proxy_sizes);
} }
void IMB_free_indices(ImBufAnim *anim) void IMB_free_indices(ImBufAnim *anim)

View File

@ -299,62 +299,19 @@ static int isffmpeg(const char *filepath)
} }
#endif #endif
ImbAnimType imb_get_anim_type(const char *filepath) bool IMB_isanim(const char *filepath)
{ {
BLI_stat_t st;
BLI_assert(!BLI_path_is_rel(filepath)); BLI_assert(!BLI_path_is_rel(filepath));
if (UTIL_DEBUG) { if (UTIL_DEBUG) {
printf("%s: %s\n", __func__, filepath); printf("%s: %s\n", __func__, filepath);
} }
#ifndef _WIN32 #ifdef WITH_FFMPEG
# ifdef WITH_FFMPEG
/* stat test below fails on large files > 4GB */
if (isffmpeg(filepath)) { if (isffmpeg(filepath)) {
return ImbAnimType::Ffmpeg; return true;
}
# endif
if (BLI_stat(filepath, &st) == -1) {
return ImbAnimType::NotAnim;
}
if (((st.st_mode) & S_IFMT) != S_IFREG) {
return ImbAnimType::NotAnim;
} }
#endif
if (ismovie(filepath)) { return false;
return ImbAnimType::Movie;
}
#else /* !_WIN32 */
if (BLI_stat(filepath, &st) == -1) {
return ImbAnimType::NotAnim;
}
if (((st.st_mode) & S_IFMT) != S_IFREG) {
return ImbAnimType::NotAnim;
}
if (ismovie(filepath)) {
return ImbAnimType::Movie;
}
# ifdef WITH_FFMPEG
if (isffmpeg(filepath)) {
return ImbAnimType::Ffmpeg;
}
# endif
#endif /* !_WIN32 */
/* Assume a single image is part of an image sequence. */
if (IMB_ispic(filepath)) {
return ImbAnimType::Sequence;
}
return ImbAnimType::NotAnim;
}
bool IMB_isanim(const char *filepath)
{
ImbAnimType type = imb_get_anim_type(filepath);
return !ELEM(type, ImbAnimType::NotAnim, ImbAnimType::Sequence);
} }

View File

@ -1112,7 +1112,7 @@ typedef enum IDRecalcFlag {
* have to be copied on every update. */ * have to be copied on every update. */
ID_RECALC_PARAMETERS = (1 << 21), ID_RECALC_PARAMETERS = (1 << 21),
/* Input has changed and datablock is to be reload from disk. /* Input has changed and data-block is to be reload from disk.
* Applies to movie clips to inform that copy-on-written version is to be refreshed for the new * Applies to movie clips to inform that copy-on-written version is to be refreshed for the new
* input file or for color space changes. */ * input file or for color space changes. */
ID_RECALC_SOURCE = (1 << 23), ID_RECALC_SOURCE = (1 << 23),

View File

@ -57,9 +57,9 @@ typedef struct bMotionPath {
/** The number of cached verts. */ /** The number of cached verts. */
int length; int length;
/** For drawing paths, the start frame number. */ /** For drawing paths, the start frame number. Inclusive.*/
int start_frame; int start_frame;
/** For drawing paths, the end frame number. */ /** For drawing paths, the end frame number. Exclusive. */
int end_frame; int end_frame;
/** Optional custom color. */ /** Optional custom color. */
@ -113,7 +113,7 @@ typedef struct bAnimVizSettings {
short path_bakeflag; short path_bakeflag;
char _pad[4]; char _pad[4];
/** Start and end frames of path-calculation range. */ /** Start and end frames of path-calculation range. Both are inclusive.*/
int path_sf, path_ef; int path_sf, path_ef;
/** Number of frames before/after current frame to show. */ /** Number of frames before/after current frame to show. */
int path_bc, path_ac; int path_bc, path_ac;

View File

@ -790,6 +790,7 @@ typedef struct bNodeTree {
bNode *group_output_node(); bNode *group_output_node();
const bNode *group_output_node() const; const bNode *group_output_node() const;
/** Get all input nodes of the node group. */ /** Get all input nodes of the node group. */
blender::Span<bNode *> group_input_nodes();
blender::Span<const bNode *> group_input_nodes() const; blender::Span<const bNode *> group_input_nodes() const;
/** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */ /** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */

View File

@ -22,6 +22,7 @@
# include <fmt/format.h> # include <fmt/format.h>
# include "BKE_attribute.hh"
# include "BKE_grease_pencil.hh" # include "BKE_grease_pencil.hh"
# include "BLI_span.hh" # include "BLI_span.hh"
@ -135,6 +136,34 @@ static void rna_GreasePencilLayer_name_set(PointerRNA *ptr, const char *value)
grease_pencil->rename_node(layer->wrap().as_node(), value); grease_pencil->rename_node(layer->wrap().as_node(), value);
} }
static int rna_GreasePencilLayer_pass_index_get(PointerRNA *ptr)
{
using namespace blender;
const GreasePencil &grease_pencil = *rna_grease_pencil(ptr);
const bke::greasepencil::Layer &layer =
static_cast<const GreasePencilLayer *>(ptr->data)->wrap();
const int layer_idx = *grease_pencil.get_layer_index(layer);
const VArray layer_passes = *grease_pencil.attributes().lookup_or_default<int>(
"pass_index", bke::AttrDomain::Layer, 0);
return layer_passes[layer_idx];
}
static void rna_GreasePencilLayer_pass_index_set(PointerRNA *ptr, int value)
{
using namespace blender;
GreasePencil &grease_pencil = *rna_grease_pencil(ptr);
const bke::greasepencil::Layer &layer =
static_cast<const GreasePencilLayer *>(ptr->data)->wrap();
const int layer_idx = *grease_pencil.get_layer_index(layer);
bke::SpanAttributeWriter<int> layer_passes =
grease_pencil.attributes_for_write().lookup_or_add_for_write_span<int>(
"pass_index", bke::AttrDomain::Layer);
layer_passes.span[layer_idx] = std::max(0, value);
layer_passes.finish();
}
static PointerRNA rna_GreasePencil_active_layer_get(PointerRNA *ptr) static PointerRNA rna_GreasePencil_active_layer_get(PointerRNA *ptr)
{ {
GreasePencil *grease_pencil = rna_grease_pencil(ptr); GreasePencil *grease_pencil = rna_grease_pencil(ptr);
@ -253,6 +282,15 @@ static void rna_def_grease_pencil_layer(BlenderRNA *brna)
prop, "Onion Skinning", "Display onion skins before and after the current frame"); prop, "Onion Skinning", "Display onion skins before and after the current frame");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
/* pass index for compositing and modifiers */
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Layer Index\" pass");
RNA_def_property_int_funcs(prop,
"rna_GreasePencilLayer_pass_index_get",
"rna_GreasePencilLayer_pass_index_set",
nullptr);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE); prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object"); RNA_def_property_struct_type(prop, "Object");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);

View File

@ -1918,6 +1918,19 @@ static std::optional<std::string> rna_ViewLayerEEVEE_path(const PointerRNA *ptr)
return rna_path; return rna_path;
} }
static void rna_SceneEEVEE_gi_cubemap_resolution_update(Main * /*main*/,
Scene *scene,
PointerRNA * /*ptr*/)
{
/* Tag all light probes to recalc transform. This signals EEVEE to update the light probes. */
FOREACH_SCENE_OBJECT_BEGIN (scene, ob) {
if (ob->type == OB_LIGHTPROBE) {
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
}
FOREACH_SCENE_OBJECT_END;
}
static std::optional<std::string> rna_SceneRenderView_path(const PointerRNA *ptr) static std::optional<std::string> rna_SceneRenderView_path(const PointerRNA *ptr)
{ {
const SceneRenderView *srv = (SceneRenderView *)ptr->data; const SceneRenderView *srv = (SceneRenderView *)ptr->data;
@ -7799,6 +7812,7 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
RNA_def_property_enum_items(prop, eevee_shadow_size_items); RNA_def_property_enum_items(prop, eevee_shadow_size_items);
RNA_def_property_ui_text(prop, "Cubemap Size", "Size of every cubemaps"); RNA_def_property_ui_text(prop, "Cubemap Size", "Size of every cubemaps");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_SceneEEVEE_gi_cubemap_resolution_update");
prop = RNA_def_property(srna, "gi_visibility_resolution", PROP_ENUM, PROP_NONE); prop = RNA_def_property(srna, "gi_visibility_resolution", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, eevee_gi_visibility_size_items); RNA_def_property_enum_items(prop, eevee_gi_visibility_size_items);

View File

@ -87,6 +87,8 @@ static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void
auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md); auto *mmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data); modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
walk(user_data, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
} }
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)

View File

@ -204,7 +204,7 @@ static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil,
bke::AttributeAccessor layer_attributes = grease_pencil.attributes(); bke::AttributeAccessor layer_attributes = grease_pencil.attributes();
const Span<const Layer *> layers = grease_pencil.layers(); const Span<const Layer *> layers = grease_pencil.layers();
const VArray<int> layer_passes = const VArray<int> layer_passes =
layer_attributes.lookup_or_default<int>("pass", bke::AttrDomain::Layer, 0).varray; layer_attributes.lookup_or_default<int>("pass_index", bke::AttrDomain::Layer, 0).varray;
IndexMask result = IndexMask::from_predicate( IndexMask result = IndexMask::from_predicate(
full_mask, GrainSize(4096), memory, [&](const int64_t layer_i) { full_mask, GrainSize(4096), memory, [&](const int64_t layer_i) {

View File

@ -240,100 +240,22 @@ CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *n
return session_ptr.release(); return session_ptr.release();
} }
namespace blender::nodes::node_composite_cryptomatte_cc { namespace blender::nodes::node_composite_base_cryptomatte_cc {
NODE_STORAGE_FUNCS(NodeCryptomatte) NODE_STORAGE_FUNCS(NodeCryptomatte)
static bNodeSocketTemplate cmp_node_cryptomatte_out[] = {
{SOCK_RGBA, N_("Image")},
{SOCK_FLOAT, N_("Matte")},
{SOCK_RGBA, N_("Pick")},
{-1, ""},
};
static void cmp_node_cryptomatte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>("Image")
.default_value({0.0f, 0.0f, 0.0f, 1.0f})
.compositor_domain_priority(0);
b.add_output<decl::Color>("Image");
b.add_output<decl::Float>("Matte");
b.add_output<decl::Color>("Pick");
}
static void node_init_cryptomatte(bNodeTree * /*ntree*/, bNode *node)
{
NodeCryptomatte *user = MEM_cnew<NodeCryptomatte>(__func__);
node->storage = user;
}
static void node_init_api_cryptomatte(const bContext *C, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
bNode *node = static_cast<bNode *>(ptr->data);
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
node->id = &scene->id;
id_us_plus(node->id);
}
static void node_free_cryptomatte(bNode *node)
{
BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY));
NodeCryptomatte *nc = static_cast<NodeCryptomatte *>(node->storage);
if (nc) {
MEM_SAFE_FREE(nc->matte_id);
BLI_freelistN(&nc->runtime.layers);
BLI_freelistN(&nc->entries);
MEM_freeN(nc);
}
}
static void node_copy_cryptomatte(bNodeTree * /*dst_ntree*/,
bNode *dest_node,
const bNode *src_node)
{
NodeCryptomatte *src_nc = static_cast<NodeCryptomatte *>(src_node->storage);
NodeCryptomatte *dest_nc = static_cast<NodeCryptomatte *>(MEM_dupallocN(src_nc));
BLI_duplicatelist(&dest_nc->entries, &src_nc->entries);
BLI_listbase_clear(&dest_nc->runtime.layers);
dest_nc->matte_id = static_cast<char *>(MEM_dupallocN(src_nc->matte_id));
dest_node->storage = dest_nc;
}
static bool node_poll_cryptomatte(const bNodeType * /*ntype*/,
const bNodeTree *ntree,
const char **r_disabled_hint)
{
if (STREQ(ntree->idname, "CompositorNodeTree")) {
Scene *scene;
/* See node_composit_poll_rlayers. */
for (scene = static_cast<Scene *>(G.main->scenes.first); scene;
scene = static_cast<Scene *>(scene->id.next))
{
if (scene->nodetree == ntree) {
break;
}
}
if (scene == nullptr) {
*r_disabled_hint = RPT_(
"The node tree must be the compositing node tree of any scene in the file");
}
return scene != nullptr;
}
*r_disabled_hint = RPT_("Not a compositor node tree");
return false;
}
using namespace blender::realtime_compositor; using namespace blender::realtime_compositor;
class CryptoMatteOperation : public NodeOperation { class BaseCryptoMatteOperation : public NodeOperation {
public: public:
using NodeOperation::NodeOperation; using NodeOperation::NodeOperation;
/* Should return the input image result. */
virtual Result &get_input_image() = 0;
/* Should returns all the Cryptomatte layers in order. */
virtual Vector<GPUTexture *> get_layers() = 0;
void execute() override void execute() override
{ {
Vector<GPUTexture *> layers = get_layers(); Vector<GPUTexture *> layers = get_layers();
@ -462,7 +384,7 @@ class CryptoMatteOperation : public NodeOperation {
GPUShader *shader = context().get_shader("compositor_cryptomatte_image"); GPUShader *shader = context().get_shader("compositor_cryptomatte_image");
GPU_shader_bind(shader); GPU_shader_bind(shader);
Result &input_image = get_input("Image"); Result &input_image = get_input_image();
input_image.bind_as_texture(shader, "input_tx"); input_image.bind_as_texture(shader, "input_tx");
matte.bind_as_texture(shader, "matte_tx"); matte.bind_as_texture(shader, "matte_tx");
@ -480,8 +402,123 @@ class CryptoMatteOperation : public NodeOperation {
image_output.unbind_as_image(); image_output.unbind_as_image();
} }
/* Get the identifiers of the entities selected by the user to generate a matte from. The
* identifiers are hashes of the names of the entities encoded in floats. See the "ID Generation"
* section of the Cryptomatte specification for more information. */
Vector<float> get_identifiers()
{
Vector<float> identifiers;
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &node_storage(bnode()).entries) {
identifiers.append(cryptomatte_entry->encoded_hash);
}
return identifiers;
}
};
} // namespace blender::nodes::node_composite_base_cryptomatte_cc
namespace blender::nodes::node_composite_cryptomatte_cc {
NODE_STORAGE_FUNCS(NodeCryptomatte)
static bNodeSocketTemplate cmp_node_cryptomatte_out[] = {
{SOCK_RGBA, N_("Image")},
{SOCK_FLOAT, N_("Matte")},
{SOCK_RGBA, N_("Pick")},
{-1, ""},
};
static void cmp_node_cryptomatte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>("Image")
.default_value({0.0f, 0.0f, 0.0f, 1.0f})
.compositor_domain_priority(0);
b.add_output<decl::Color>("Image");
b.add_output<decl::Float>("Matte");
b.add_output<decl::Color>("Pick");
}
static void node_init_cryptomatte(bNodeTree * /*ntree*/, bNode *node)
{
NodeCryptomatte *user = MEM_cnew<NodeCryptomatte>(__func__);
node->storage = user;
}
static void node_init_api_cryptomatte(const bContext *C, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
bNode *node = static_cast<bNode *>(ptr->data);
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
node->id = &scene->id;
id_us_plus(node->id);
}
static void node_free_cryptomatte(bNode *node)
{
BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY));
NodeCryptomatte *nc = static_cast<NodeCryptomatte *>(node->storage);
if (nc) {
MEM_SAFE_FREE(nc->matte_id);
BLI_freelistN(&nc->runtime.layers);
BLI_freelistN(&nc->entries);
MEM_freeN(nc);
}
}
static void node_copy_cryptomatte(bNodeTree * /*dst_ntree*/,
bNode *dest_node,
const bNode *src_node)
{
NodeCryptomatte *src_nc = static_cast<NodeCryptomatte *>(src_node->storage);
NodeCryptomatte *dest_nc = static_cast<NodeCryptomatte *>(MEM_dupallocN(src_nc));
BLI_duplicatelist(&dest_nc->entries, &src_nc->entries);
BLI_listbase_clear(&dest_nc->runtime.layers);
dest_nc->matte_id = static_cast<char *>(MEM_dupallocN(src_nc->matte_id));
dest_node->storage = dest_nc;
}
static bool node_poll_cryptomatte(const bNodeType * /*ntype*/,
const bNodeTree *ntree,
const char **r_disabled_hint)
{
if (STREQ(ntree->idname, "CompositorNodeTree")) {
Scene *scene;
/* See node_composit_poll_rlayers. */
for (scene = static_cast<Scene *>(G.main->scenes.first); scene;
scene = static_cast<Scene *>(scene->id.next))
{
if (scene->nodetree == ntree) {
break;
}
}
if (scene == nullptr) {
*r_disabled_hint = RPT_(
"The node tree must be the compositing node tree of any scene in the file");
}
return scene != nullptr;
}
*r_disabled_hint = RPT_("Not a compositor node tree");
return false;
}
using namespace blender::realtime_compositor;
using namespace blender::nodes::node_composite_base_cryptomatte_cc;
class CryptoMatteOperation : public BaseCryptoMatteOperation {
public:
using BaseCryptoMatteOperation::BaseCryptoMatteOperation;
Result &get_input_image() override
{
return get_input("Image");
}
/* Returns all the relevant Cryptomatte layers from the selected source. */ /* Returns all the relevant Cryptomatte layers from the selected source. */
Vector<GPUTexture *> get_layers() Vector<GPUTexture *> get_layers() override
{ {
switch (get_source()) { switch (get_source()) {
case CMP_NODE_CRYPTOMATTE_SOURCE_RENDER: case CMP_NODE_CRYPTOMATTE_SOURCE_RENDER:
@ -631,18 +668,6 @@ class CryptoMatteOperation : public NodeOperation {
return std::string(type_name); return std::string(type_name);
} }
/* Get the identifiers of the entities selected by the user to generate a matte from. The
* identifiers are hashes of the names of the entities encoded in floats. See the "ID Generation"
* section of the Cryptomatte specification for more information. */
Vector<float> get_identifiers()
{
Vector<float> identifiers;
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &node_storage(bnode()).entries) {
identifiers.append(cryptomatte_entry->encoded_hash);
}
return identifiers;
}
/* The domain should be centered with the same size as the source. In case of invalid source, /* The domain should be centered with the same size as the source. In case of invalid source,
* fallback to the domain inferred from the input. */ * fallback to the domain inferred from the input. */
Domain compute_domain() override Domain compute_domain() override
@ -804,23 +829,31 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
} }
using namespace blender::realtime_compositor; using namespace blender::realtime_compositor;
using namespace blender::nodes::node_composite_base_cryptomatte_cc;
class CryptoMatteOperation : public NodeOperation { class LegacyCryptoMatteOperation : public BaseCryptoMatteOperation {
public: public:
using NodeOperation::NodeOperation; using BaseCryptoMatteOperation::BaseCryptoMatteOperation;
void execute() override Result &get_input_image() override
{ {
get_input("image").pass_through(get_result("Image")); return get_input("image");
get_result("Matte").allocate_invalid(); }
get_result("Pick").allocate_invalid();
context().set_info_message("Viewport compositor setup not fully supported"); Vector<GPUTexture *> get_layers() override
{
Vector<GPUTexture *> layers;
/* Add all textures of all inputs except the first input, which is the input image. */
for (const bNodeSocket *socket : bnode().input_sockets().drop_front(1)) {
layers.append(get_input(socket->identifier).texture());
}
return layers;
} }
}; };
static NodeOperation *get_compositor_operation(Context &context, DNode node) static NodeOperation *get_compositor_operation(Context &context, DNode node)
{ {
return new CryptoMatteOperation(context, node); return new LegacyCryptoMatteOperation(context, node);
} }
} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc } // namespace blender::nodes::node_composite_legacy_cryptomatte_cc
@ -840,8 +873,6 @@ void register_node_type_cmp_cryptomatte_legacy()
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
ntype.gather_link_search_ops = nullptr; ntype.gather_link_search_ops = nullptr;
ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation; ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation;
ntype.realtime_compositor_unsupported_message = N_(
"Node not supported in the Viewport compositor");
nodeRegisterType(&ntype); nodeRegisterType(&ntype);
} }

View File

@ -2503,6 +2503,7 @@ static eHandlerActionFlag wm_handler_operator_call(bContext *C,
else { else {
/* Not very common, but modal operators may report before finishing. */ /* Not very common, but modal operators may report before finishing. */
if (!BLI_listbase_is_empty(&op->reports->list)) { if (!BLI_listbase_is_empty(&op->reports->list)) {
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, nullptr);
WM_reports_from_reports_move(wm, op->reports); WM_reports_from_reports_move(wm, op->reports);
} }
} }

View File

@ -1703,10 +1703,6 @@ install(
set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/release/datafiles/assets/publish/) set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/release/datafiles/assets/publish/)
if(NOT EXISTS "${ASSET_BUNDLE_DIR}")
set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/../lib/assets/publish/)
endif()
# TODO temporary change for development only. Remove before merging. # TODO temporary change for development only. Remove before merging.
set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/") set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/")
if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}") if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}")

1
tests/data Submodule

@ -0,0 +1 @@
Subproject commit 74d32aff48a05236adf61a24d8c1b5b4c7c55097

View File

@ -14,7 +14,7 @@ namespace blender::tests {
/* These strings are passed on the CLI with the --test-asset-dir and --test-release-dir arguments. /* These strings are passed on the CLI with the --test-asset-dir and --test-release-dir arguments.
* The arguments are added automatically when invoking tests via `ctest`. */ * The arguments are added automatically when invoking tests via `ctest`. */
const std::string &flags_test_asset_dir(); /* ../lib/tests in the SVN directory. */ const std::string &flags_test_asset_dir(); /* tests/data in the Blender repository. */
const std::string &flags_test_release_dir(); /* bin/{blender version} in the build directory. */ const std::string &flags_test_release_dir(); /* bin/{blender version} in the build directory. */
} // namespace blender::tests } // namespace blender::tests

View File

@ -6,7 +6,7 @@
#include "MEM_guardedalloc.h" #include "MEM_guardedalloc.h"
DEFINE_string(test_assets_dir, "", "lib/tests directory from SVN containing the test assets."); DEFINE_string(test_assets_dir, "", "tests/data directory containing the test assets.");
DEFINE_string(test_release_dir, "", "bin/{blender version} directory of the current build."); DEFINE_string(test_release_dir, "", "bin/{blender version} directory of the current build.");
namespace blender::tests { namespace blender::tests {
@ -14,8 +14,7 @@ namespace blender::tests {
const std::string &flags_test_asset_dir() const std::string &flags_test_asset_dir()
{ {
if (FLAGS_test_assets_dir.empty()) { if (FLAGS_test_assets_dir.empty()) {
ADD_FAILURE() ADD_FAILURE() << "Pass the flag --test-assets-dir and point to the tests/data directory.";
<< "Pass the flag --test-assets-dir and point to the lib/tests directory from SVN.";
} }
return FLAGS_test_assets_dir; return FLAGS_test_assets_dir;
} }

View File

@ -26,7 +26,7 @@ class TestEnvironment:
self.build_dir = base_dir / 'build' self.build_dir = base_dir / 'build'
self.install_dir = self.build_dir / "bin" self.install_dir = self.build_dir / "bin"
self.lib_dir = base_dir / 'lib' self.lib_dir = base_dir / 'lib'
self.benchmarks_dir = self.blender_git_dir.parent / 'lib' / 'benchmarks' self.benchmarks_dir = self.blender_git_dir / 'tests' / 'benchmarks'
self.git_executable = 'git' self.git_executable = 'git'
self.cmake_executable = 'cmake' self.cmake_executable = 'cmake'
self.cmake_options = ['-DWITH_INTERNATIONAL=OFF', '-DWITH_BUILDINFO=OFF'] self.cmake_options = ['-DWITH_INTERNATIONAL=OFF', '-DWITH_BUILDINFO=OFF']

View File

@ -8,7 +8,7 @@
# and don't give deterministic results # and don't give deterministic results
set(USE_EXPERIMENTAL_TESTS FALSE) set(USE_EXPERIMENTAL_TESTS FALSE)
set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/tests/data)
set(TEST_PYTHON_DIR ${CMAKE_SOURCE_DIR}/tests/python) set(TEST_PYTHON_DIR ${CMAKE_SOURCE_DIR}/tests/python)
set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests)

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
""" """
./blender.bin --background --factory-startup --python tests/python/bl_alembic_io_test.py -- --testdir /path/to/lib/tests/alembic ./blender.bin --background --factory-startup --python tests/python/bl_alembic_io_test.py -- --testdir /path/to/tests/data/alembic
""" """
import math import math

View File

@ -9,7 +9,7 @@ import sys
from rna_prop_ui import rna_idprop_quote_path from rna_prop_ui import rna_idprop_quote_path
""" """
blender -b --factory-startup --python tests/python/bl_animation_drivers.py -- --testdir /path/to/lib/tests/animation blender -b --factory-startup --python tests/python/bl_animation_drivers.py -- --testdir /path/to/tests/data/animation
""" """

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
""" """
blender -b --factory-startup --python tests/python/bl_animation_fcurves.py -- --testdir /path/to/lib/tests/animation blender -b --factory-startup --python tests/python/bl_animation_fcurves.py -- --testdir /path/to/tests/data/animation
""" """
import pathlib import pathlib

View File

@ -9,7 +9,7 @@ import sys
from math import radians from math import radians
""" """
blender -b --factory-startup --python tests/python/bl_animation_keyframing.py -- --testdir /path/to/lib/tests/animation blender -b --factory-startup --python tests/python/bl_animation_keyframing.py -- --testdir /path/to/tests/data/animation
""" """

View File

@ -75,7 +75,7 @@ def argparse_create():
# When --help or no args are given, print this help # When --help or no args are given, print this help
description = ("Test basic versioning code by opening all blend files " description = ("Test basic versioning code by opening all blend files "
"in `lib/tests` directory.") "in `tests/data` directory.")
parser = argparse.ArgumentParser(description=description) parser = argparse.ArgumentParser(description=description)
parser.add_argument( parser.add_argument(
"--src-test-dir", "--src-test-dir",

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
""" """
./blender.bin --background --factory-startup --python tests/python/bl_constraints.py -- --testdir /path/to/lib/tests/constraints ./blender.bin --background --factory-startup --python tests/python/bl_constraints.py -- --testdir /path/to/tests/data/constraints
""" """
import pathlib import pathlib

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
""" """
blender -b --factory-startup --python tests/python/bl_rigging_symmetrize.py -- --testdir /path/to/lib/tests/animation blender -b --factory-startup --python tests/python/bl_rigging_symmetrize.py -- --testdir /path/to/tests/data/animation
""" """
import pathlib import pathlib

View File

@ -10,7 +10,7 @@
# and don't give deterministic results # and don't give deterministic results
set(USE_EXPERIMENTAL_TESTS FALSE) set(USE_EXPERIMENTAL_TESTS FALSE)
set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/tests/data)
set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests)
# ugh, any better way to do this on testing only? # ugh, any better way to do this on testing only?

View File

@ -5,7 +5,7 @@
""" """
Call as follows: Call as follows:
python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir tests/data/collada/mesh
""" """
import sys import sys

View File

@ -5,7 +5,7 @@
""" """
Call as follows: Call as follows:
python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir tests/data/collada/mesh
""" """
import sys import sys

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
# To run all tests, use # To run all tests, use
# BLENDER_VERBOSE=1 ./bin/blender ../lib/tests/modeling/curve_to_mesh.blend --python ../blender/tests/python/bl_curve_to_mesh.py -- --run-all-tests # BLENDER_VERBOSE=1 ./bin/blender ../tests/data/modeling/curve_to_mesh.blend --python ../blender/tests/python/bl_curve_to_mesh.py -- --run-all-tests
# (that assumes the test is run from a build directory in the same directory as the source code) # (that assumes the test is run from a build directory in the same directory as the source code)
import bpy import bpy
import os import os

View File

@ -6,7 +6,7 @@
# Use '--write-blend=/tmp/test.blend' to view output # Use '--write-blend=/tmp/test.blend' to view output
set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/tests/data)
set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests)
# ugh, any better way to do this on testing only? # ugh, any better way to do this on testing only?