Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
8189f2c806
13
GNUmakefile
13
GNUmakefile
|
@ -128,6 +128,9 @@ Utilities
|
|||
* source_archive:
|
||||
Create a compressed archive of the source code.
|
||||
|
||||
* source_archive_complete:
|
||||
Create a compressed archive of the source code and all the libraries of dependencies.
|
||||
|
||||
* update:
|
||||
Updates git and all submodules and svn.
|
||||
|
||||
|
@ -477,6 +480,9 @@ check_smatch: .FORCE
|
|||
cd "$(BUILD_DIR)" ; \
|
||||
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_smatch.py"
|
||||
|
||||
check_mypy: .FORCE
|
||||
$(PYTHON) "$(BLENDER_DIR)/source/tools/check_source/check_mypy.py"
|
||||
|
||||
check_spelling_py: .FORCE
|
||||
cd "$(BUILD_DIR)" ; \
|
||||
PYTHONIOENCODING=utf_8 $(PYTHON) \
|
||||
|
@ -511,6 +517,13 @@ check_descriptions: .FORCE
|
|||
source_archive: .FORCE
|
||||
python3 ./build_files/utils/make_source_archive.py
|
||||
|
||||
source_archive_complete: .FORCE
|
||||
cmake -S "$(BLENDER_DIR)/build_files/build_environment" -B"$(BUILD_DIR)/source_archive" \
|
||||
-DCMAKE_BUILD_TYPE_INIT:STRING=$(BUILD_TYPE) -DPACKAGE_USE_UPSTREAM_SOURCES=OFF
|
||||
# This assumes CMake is still using a default `PACKAGE_DIR` variable:
|
||||
python3 ./build_files/utils/make_source_archive.py --include-packages "$(BUILD_DIR)/source_archive/packages"
|
||||
|
||||
|
||||
INKSCAPE_BIN?="inkscape"
|
||||
icons: .FORCE
|
||||
BLENDER_BIN=$(BLENDER_BIN) INKSCAPE_BIN=$(INKSCAPE_BIN) \
|
||||
|
|
|
@ -12,7 +12,7 @@ function(download_source dep)
|
|||
if(NOT EXISTS ${TARGET_FILE})
|
||||
message("Checking source : ${dep} - source not found downloading from ${TARGET_URI}")
|
||||
file(DOWNLOAD ${TARGET_URI} ${TARGET_FILE}
|
||||
TIMEOUT 60 # seconds
|
||||
TIMEOUT 1800 # seconds
|
||||
EXPECTED_HASH ${TARGET_HASH_TYPE}=${TARGET_HASH}
|
||||
TLS_VERIFY ON
|
||||
SHOW_PROGRESS
|
||||
|
|
|
@ -68,7 +68,6 @@ set(OPENIMAGEIO_EXTRA_ARGS
|
|||
-DBOOST_LIBRARYDIR=${LIBDIR}/boost/lib/
|
||||
-DBoost_NO_SYSTEM_PATHS=ON
|
||||
-DBoost_NO_BOOST_CMAKE=ON
|
||||
-OIIO_BUILD_CPP11=ON
|
||||
-DUSE_LIBSQUISH=OFF
|
||||
-DUSE_QT5=OFF
|
||||
-DUSE_NUKE=OFF
|
||||
|
|
|
@ -37,14 +37,8 @@ else(BUILD_MODE STREQUAL "Debug")
|
|||
endif()
|
||||
|
||||
set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" CACHE STRING "Path for downloaded files")
|
||||
# look in blenders source folder for packages directory, if that exists
|
||||
# it will our package folder, otherwise it will be in the build folder
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/../../packages")
|
||||
set(PACKAGE_DIR_DEFAULT "${CMAKE_SOURCE_DIR}/../../packages")
|
||||
else()
|
||||
set(PACKAGE_DIR_DEFAULT "${CMAKE_CURRENT_BINARY_DIR}/packages")
|
||||
endif()
|
||||
set(PACKAGE_DIR ${PACKAGE_DIR_DEFAULT} CACHE STRING "Path for downloaded source files")
|
||||
# This path must be hard-coded like this, so that the GNUmakefile knows where it is and can pass it to make_source_archive.py:
|
||||
set(PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/packages")
|
||||
option(PACKAGE_USE_UPSTREAM_SOURCES "Use soures upstream to download the package sources, when OFF the blender mirror will be used" ON)
|
||||
|
||||
file(TO_CMAKE_PATH ${DOWNLOAD_DIR} DOWNLOAD_DIR)
|
||||
|
|
|
@ -28,6 +28,14 @@ if sys.version_info.major < 3:
|
|||
sys.version.partition(" ")[0])
|
||||
sys.exit(1)
|
||||
|
||||
import os
|
||||
from os.path import (
|
||||
dirname,
|
||||
join,
|
||||
normpath,
|
||||
splitext,
|
||||
)
|
||||
|
||||
from cmake_consistency_check_config import (
|
||||
IGNORE_SOURCE,
|
||||
IGNORE_SOURCE_MISSING,
|
||||
|
@ -37,32 +45,35 @@ from cmake_consistency_check_config import (
|
|||
BUILD_DIR,
|
||||
)
|
||||
|
||||
|
||||
import os
|
||||
from os.path import (
|
||||
dirname,
|
||||
join,
|
||||
normpath,
|
||||
splitext,
|
||||
from typing import (
|
||||
Callable,
|
||||
Dict,
|
||||
Generator,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
|
||||
global_h = set()
|
||||
global_c = set()
|
||||
global_refs = {}
|
||||
global_refs: Dict[str, List[Tuple[str, int]]] = {}
|
||||
|
||||
# Flatten `IGNORE_SOURCE_MISSING` to avoid nested looping.
|
||||
IGNORE_SOURCE_MISSING = [
|
||||
IGNORE_SOURCE_MISSING_FLAT = [
|
||||
(k, ignore_path) for k, ig_list in IGNORE_SOURCE_MISSING
|
||||
for ignore_path in ig_list
|
||||
]
|
||||
|
||||
# Ignore cmake file, path pairs.
|
||||
global_ignore_source_missing = {}
|
||||
for k, v in IGNORE_SOURCE_MISSING:
|
||||
global_ignore_source_missing: Dict[str, List[str]] = {}
|
||||
for k, v in IGNORE_SOURCE_MISSING_FLAT:
|
||||
global_ignore_source_missing.setdefault(k, []).append(v)
|
||||
del IGNORE_SOURCE_MISSING_FLAT
|
||||
|
||||
|
||||
def replace_line(f, i, text, keep_indent=True):
|
||||
def replace_line(f: str, i: int, text: str, keep_indent: bool = True) -> None:
|
||||
file_handle = open(f, 'r')
|
||||
data = file_handle.readlines()
|
||||
file_handle.close()
|
||||
|
@ -77,7 +88,10 @@ def replace_line(f, i, text, keep_indent=True):
|
|||
file_handle.close()
|
||||
|
||||
|
||||
def source_list(path, filename_check=None):
|
||||
def source_list(
|
||||
path: str,
|
||||
filename_check: Optional[Callable[[str], bool]] = None,
|
||||
) -> Generator[str, None, None]:
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
# skip '.git'
|
||||
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
||||
|
@ -88,37 +102,37 @@ def source_list(path, filename_check=None):
|
|||
|
||||
|
||||
# extension checking
|
||||
def is_cmake(filename):
|
||||
def is_cmake(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext == ".cmake") or (filename == "CMakeLists.txt")
|
||||
|
||||
|
||||
def is_c_header(filename):
|
||||
def is_c_header(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in {".h", ".hpp", ".hxx", ".hh"})
|
||||
|
||||
|
||||
def is_c(filename):
|
||||
def is_c(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl"})
|
||||
|
||||
|
||||
def is_c_any(filename):
|
||||
def is_c_any(filename: str) -> bool:
|
||||
return is_c(filename) or is_c_header(filename)
|
||||
|
||||
|
||||
def cmake_get_src(f):
|
||||
def cmake_get_src(f: str) -> None:
|
||||
|
||||
sources_h = []
|
||||
sources_c = []
|
||||
|
||||
filen = open(f, "r", encoding="utf8")
|
||||
it = iter(filen)
|
||||
it: Optional[Iterator[str]] = iter(filen)
|
||||
found = False
|
||||
i = 0
|
||||
# print(f)
|
||||
|
||||
def is_definition(l, f, i, name):
|
||||
def is_definition(l: str, f: str, i: int, name: str) -> bool:
|
||||
if l.startswith("unset("):
|
||||
return False
|
||||
|
||||
|
@ -131,6 +145,7 @@ def cmake_get_src(f):
|
|||
if l.endswith(")"):
|
||||
raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i))
|
||||
return True
|
||||
return False
|
||||
|
||||
while it is not None:
|
||||
context_name = ""
|
||||
|
@ -269,7 +284,7 @@ def cmake_get_src(f):
|
|||
filen.close()
|
||||
|
||||
|
||||
def is_ignore_source(f, ignore_used):
|
||||
def is_ignore_source(f: str, ignore_used: List[bool]) -> bool:
|
||||
for index, ignore_path in enumerate(IGNORE_SOURCE):
|
||||
if ignore_path in f:
|
||||
ignore_used[index] = True
|
||||
|
@ -277,7 +292,7 @@ def is_ignore_source(f, ignore_used):
|
|||
return False
|
||||
|
||||
|
||||
def is_ignore_cmake(f, ignore_used):
|
||||
def is_ignore_cmake(f: str, ignore_used: List[bool]) -> bool:
|
||||
for index, ignore_path in enumerate(IGNORE_CMAKE):
|
||||
if ignore_path in f:
|
||||
ignore_used[index] = True
|
||||
|
@ -285,7 +300,7 @@ def is_ignore_cmake(f, ignore_used):
|
|||
return False
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
|
||||
print("Scanning:", SOURCE_DIR)
|
||||
|
||||
|
@ -359,7 +374,7 @@ def main():
|
|||
if "extern" not in f:
|
||||
i = 1
|
||||
try:
|
||||
for l in open(f, "r", encoding="utf8"):
|
||||
for _ in open(f, "r", encoding="utf8"):
|
||||
i += 1
|
||||
except UnicodeDecodeError:
|
||||
print("Non utf8: %s:%d" % (f, i))
|
||||
|
|
|
@ -25,6 +25,14 @@ import subprocess
|
|||
import sys
|
||||
import os
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
List,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
|
||||
USE_QUIET = (os.environ.get("QUIET", None) is not None)
|
||||
|
||||
CHECKER_IGNORE_PREFIX = [
|
||||
|
@ -43,7 +51,7 @@ CHECKER_ARGS = [
|
|||
]
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
|
||||
|
||||
check_commands = []
|
||||
|
@ -52,18 +60,19 @@ def main():
|
|||
# ~if "source/blender" not in c:
|
||||
# ~ continue
|
||||
|
||||
cmd = ([CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs]
|
||||
)
|
||||
cmd = (
|
||||
[CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs]
|
||||
)
|
||||
|
||||
check_commands.append((c, cmd))
|
||||
|
||||
process_functions = []
|
||||
|
||||
def my_process(i, c, cmd):
|
||||
def my_process(i: int, c: str, cmd: str) -> subprocess.Popen[Any]:
|
||||
if not USE_QUIET:
|
||||
percent = 100.0 * (i / (len(check_commands) - 1))
|
||||
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
|
||||
|
|
|
@ -25,6 +25,12 @@ import subprocess
|
|||
import sys
|
||||
import os
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
List,
|
||||
)
|
||||
|
||||
|
||||
USE_QUIET = (os.environ.get("QUIET", None) is not None)
|
||||
|
||||
CHECKER_IGNORE_PREFIX = [
|
||||
|
@ -47,25 +53,26 @@ if USE_QUIET:
|
|||
CHECKER_ARGS.append("--quiet")
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
|
||||
source_defines = project_source_info.build_defines_as_args()
|
||||
|
||||
check_commands = []
|
||||
for c, inc_dirs, defs in source_info:
|
||||
cmd = ([CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs] +
|
||||
source_defines
|
||||
)
|
||||
cmd = (
|
||||
[CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs] +
|
||||
source_defines
|
||||
)
|
||||
|
||||
check_commands.append((c, cmd))
|
||||
|
||||
process_functions = []
|
||||
|
||||
def my_process(i, c, cmd):
|
||||
def my_process(i: int, c: str, cmd: List[str]) -> subprocess.Popen[Any]:
|
||||
if not USE_QUIET:
|
||||
percent = 100.0 * (i / len(check_commands))
|
||||
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
def print_help(targets):
|
||||
print("CMake quicky wrapper, no valid targets given.")
|
||||
print(" * targets can contain a subset of the full target name.")
|
||||
print(" * arguments with a '-' prefix are passed onto make.")
|
||||
print(" * this must run from the cmake build dir")
|
||||
print(" * alias this with a short command for speedy access, in bash:")
|
||||
print(" alias mk='../blender/build_files/cmake/example_scripts/make_quicky.py'")
|
||||
print("")
|
||||
print(" eg: make_quicky.py -j3 extern python")
|
||||
print(" ...will execute")
|
||||
print(" make -j3 extern_binreloc extern_glew bf_python bf_python_ext blender/fast")
|
||||
print("")
|
||||
print("Target List:")
|
||||
for t in targets:
|
||||
print(" %s" % t)
|
||||
print("...exiting")
|
||||
|
||||
|
||||
def main():
|
||||
targets = set()
|
||||
|
||||
# collect targets
|
||||
makefile = open("Makefile", "r")
|
||||
for line in makefile:
|
||||
line = line.rstrip()
|
||||
if not line or line[0] in ". \t@$#":
|
||||
continue
|
||||
|
||||
line = line.split("#", 1)[0]
|
||||
if ":" not in line:
|
||||
continue
|
||||
|
||||
line = line.split(":", 1)[0]
|
||||
|
||||
if "/" in line: # cmake terget options, dont need these
|
||||
continue
|
||||
|
||||
targets.add(line)
|
||||
makefile.close()
|
||||
|
||||
# remove cmake targets
|
||||
bad = set([
|
||||
"help",
|
||||
"clean",
|
||||
"all",
|
||||
"preinstall",
|
||||
"install",
|
||||
"default_target",
|
||||
"edit_cache",
|
||||
"cmake_force",
|
||||
"rebuild_cache",
|
||||
"depend",
|
||||
"cmake_check_build_system",
|
||||
])
|
||||
|
||||
targets -= set(bad)
|
||||
|
||||
# parse args
|
||||
targets = list(targets)
|
||||
targets.sort()
|
||||
|
||||
import sys
|
||||
if len(sys.argv) == 1:
|
||||
print_help(targets)
|
||||
return
|
||||
|
||||
targets_new = []
|
||||
args = []
|
||||
for arg in sys.argv[1:]:
|
||||
if arg[0] in "/-":
|
||||
args.append(arg)
|
||||
else:
|
||||
found = False
|
||||
for t in targets:
|
||||
if arg in t and t not in targets_new:
|
||||
targets_new.append(t)
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print("Error '%s' not found in...")
|
||||
for t in targets:
|
||||
print(" %s" % t)
|
||||
print("...aborting.")
|
||||
return
|
||||
|
||||
# execute
|
||||
cmd = ["make"] + args + targets_new + ["blender/fast"]
|
||||
print("cmake building with targets: %s" % " ".join(targets_new))
|
||||
print("executing: %s" % " ".join(cmd))
|
||||
|
||||
import subprocess
|
||||
subprocess.call(cmd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -923,6 +923,10 @@ function(get_blender_version)
|
|||
math(EXPR _out_version_major "${_out_version} / 100")
|
||||
math(EXPR _out_version_minor "${_out_version} % 100")
|
||||
|
||||
# Zero pad the minor version so `_out_version_minor` is always two characters.
|
||||
# This is needed if the minor version is a single digit.
|
||||
string(REGEX REPLACE "^([0-9])$" "0\\1" _out_version_minor "${_out_version_minor}")
|
||||
|
||||
# output vars
|
||||
set(BLENDER_VERSION "${_out_version_major}.${_out_version_minor}" PARENT_SCOPE)
|
||||
set(BLENDER_VERSION_MAJOR "${_out_version_major}" PARENT_SCOPE)
|
||||
|
|
|
@ -44,6 +44,15 @@ __all__ = (
|
|||
"init",
|
||||
)
|
||||
|
||||
from typing import (
|
||||
Callable,
|
||||
Generator,
|
||||
List,
|
||||
Optional,
|
||||
Union,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
|
||||
import sys
|
||||
if sys.version_info.major < 3:
|
||||
|
@ -70,10 +79,11 @@ SOURCE_DIR = abspath(SOURCE_DIR)
|
|||
SIMPLE_PROJECTFILE = False
|
||||
|
||||
# must initialize from 'init'
|
||||
CMAKE_DIR = None
|
||||
CMAKE_DIR = ""
|
||||
PROJECT_DIR = ""
|
||||
|
||||
|
||||
def init(cmake_path):
|
||||
def init(cmake_path: str) -> bool:
|
||||
global CMAKE_DIR, PROJECT_DIR
|
||||
|
||||
# get cmake path
|
||||
|
@ -91,7 +101,10 @@ def init(cmake_path):
|
|||
return True
|
||||
|
||||
|
||||
def source_list(path, filename_check=None):
|
||||
def source_list(
|
||||
path: str,
|
||||
filename_check: Optional[Callable[[str], bool]] = None,
|
||||
) -> Generator[str, None, None]:
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
# skip '.git'
|
||||
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
||||
|
@ -103,53 +116,57 @@ def source_list(path, filename_check=None):
|
|||
|
||||
|
||||
# extension checking
|
||||
def is_cmake(filename):
|
||||
def is_cmake(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext == ".cmake") or (filename.endswith("CMakeLists.txt"))
|
||||
|
||||
|
||||
def is_c_header(filename):
|
||||
def is_c_header(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in {".h", ".hpp", ".hxx", ".hh"})
|
||||
|
||||
|
||||
def is_py(filename):
|
||||
def is_py(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext == ".py")
|
||||
|
||||
|
||||
def is_glsl(filename):
|
||||
def is_glsl(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext == ".glsl")
|
||||
|
||||
|
||||
def is_c(filename):
|
||||
def is_c(filename: str) -> bool:
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".osl"})
|
||||
|
||||
|
||||
def is_c_any(filename):
|
||||
def is_c_any(filename: str) -> bool:
|
||||
return is_c(filename) or is_c_header(filename)
|
||||
|
||||
|
||||
def is_svn_file(filename):
|
||||
def is_svn_file(filename: str) -> bool:
|
||||
dn, fn = os.path.split(filename)
|
||||
filename_svn = join(dn, ".svn", "text-base", "%s.svn-base" % fn)
|
||||
return exists(filename_svn)
|
||||
|
||||
|
||||
def is_project_file(filename):
|
||||
def is_project_file(filename: str) -> bool:
|
||||
return (is_c_any(filename) or is_cmake(filename) or is_glsl(filename)) # and is_svn_file(filename)
|
||||
|
||||
|
||||
def cmake_advanced_info():
|
||||
def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tuple[None, None]]:
|
||||
""" Extract includes and defines from cmake.
|
||||
"""
|
||||
|
||||
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
|
||||
if make_exe is None:
|
||||
print("Make command not found in: %r not found" % project_path)
|
||||
return None, None
|
||||
|
||||
make_exe_basename = os.path.basename(make_exe)
|
||||
|
||||
def create_eclipse_project():
|
||||
def create_eclipse_project() -> str:
|
||||
print("CMAKE_DIR %r" % CMAKE_DIR)
|
||||
if sys.platform == "win32":
|
||||
raise Exception("Error: win32 is not supported")
|
||||
|
@ -219,7 +236,7 @@ def cmake_advanced_info():
|
|||
return includes, defines
|
||||
|
||||
|
||||
def cmake_cache_var(var):
|
||||
def cmake_cache_var(var: str) -> Optional[str]:
|
||||
with open(os.path.join(CMAKE_DIR, "CMakeCache.txt"), encoding='utf-8') as cache_file:
|
||||
lines = [
|
||||
l_strip for l in cache_file
|
||||
|
@ -233,12 +250,12 @@ def cmake_cache_var(var):
|
|||
return None
|
||||
|
||||
|
||||
def cmake_compiler_defines():
|
||||
def cmake_compiler_defines() -> Optional[List[str]]:
|
||||
compiler = cmake_cache_var("CMAKE_C_COMPILER") # could do CXX too
|
||||
|
||||
if compiler is None:
|
||||
print("Couldn't find the compiler, os defines will be omitted...")
|
||||
return
|
||||
return None
|
||||
|
||||
import tempfile
|
||||
temp_c = tempfile.mkstemp(suffix=".c")[1]
|
||||
|
@ -255,5 +272,5 @@ def cmake_compiler_defines():
|
|||
return lines
|
||||
|
||||
|
||||
def project_name_get():
|
||||
def project_name_get() -> Optional[str]:
|
||||
return cmake_cache_var("CMAKE_PROJECT_NAME")
|
||||
|
|
|
@ -34,30 +34,45 @@ if sys.version_info.major < 3:
|
|||
import os
|
||||
from os.path import join, dirname, normpath, abspath
|
||||
|
||||
import subprocess
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Generator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
|
||||
SOURCE_DIR = join(dirname(__file__), "..", "..")
|
||||
SOURCE_DIR = normpath(SOURCE_DIR)
|
||||
SOURCE_DIR = abspath(SOURCE_DIR)
|
||||
|
||||
|
||||
def is_c_header(filename):
|
||||
def is_c_header(filename: str) -> bool:
|
||||
ext = os.path.splitext(filename)[1]
|
||||
return (ext in {".h", ".hpp", ".hxx", ".hh"})
|
||||
|
||||
|
||||
def is_c(filename):
|
||||
def is_c(filename: str) -> bool:
|
||||
ext = os.path.splitext(filename)[1]
|
||||
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".osl"})
|
||||
|
||||
|
||||
def is_c_any(filename):
|
||||
return os.path.s_c(filename) or is_c_header(filename)
|
||||
def is_c_any(filename: str) -> bool:
|
||||
return is_c(filename) or is_c_header(filename)
|
||||
|
||||
|
||||
# copied from project_info.py
|
||||
CMAKE_DIR = "."
|
||||
|
||||
|
||||
def cmake_cache_var_iter():
|
||||
def cmake_cache_var_iter() -> Generator[Tuple[str, str, str], None, None]:
|
||||
import re
|
||||
re_cache = re.compile(r'([A-Za-z0-9_\-]+)?:?([A-Za-z0-9_\-]+)?=(.*)$')
|
||||
with open(join(CMAKE_DIR, "CMakeCache.txt"), 'r', encoding='utf-8') as cache_file:
|
||||
|
@ -68,14 +83,22 @@ def cmake_cache_var_iter():
|
|||
yield (var, type_ or "", val)
|
||||
|
||||
|
||||
def cmake_cache_var(var):
|
||||
def cmake_cache_var(var: str) -> Optional[str]:
|
||||
for var_iter, type_iter, value_iter in cmake_cache_var_iter():
|
||||
if var == var_iter:
|
||||
return value_iter
|
||||
return None
|
||||
|
||||
|
||||
def do_ignore(filepath, ignore_prefix_list):
|
||||
def cmake_cache_var_or_exit(var: str) -> str:
|
||||
value = cmake_cache_var(var)
|
||||
if value is None:
|
||||
print("Unable to find %r exiting!" % value)
|
||||
sys.exit(1)
|
||||
return value
|
||||
|
||||
|
||||
def do_ignore(filepath: str, ignore_prefix_list: Optional[Sequence[str]]) -> bool:
|
||||
if ignore_prefix_list is None:
|
||||
return False
|
||||
|
||||
|
@ -83,12 +106,13 @@ def do_ignore(filepath, ignore_prefix_list):
|
|||
return any([relpath.startswith(prefix) for prefix in ignore_prefix_list])
|
||||
|
||||
|
||||
def makefile_log():
|
||||
def makefile_log() -> List[str]:
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
# support both make and ninja
|
||||
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
|
||||
make_exe = cmake_cache_var_or_exit("CMAKE_MAKE_PROGRAM")
|
||||
|
||||
make_exe_basename = os.path.basename(make_exe)
|
||||
|
||||
if make_exe_basename.startswith(("make", "gmake")):
|
||||
|
@ -102,26 +126,37 @@ def makefile_log():
|
|||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
if process is None:
|
||||
print("Can't execute process")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
while process.poll():
|
||||
time.sleep(1)
|
||||
|
||||
out = process.stdout.read()
|
||||
process.stdout.close()
|
||||
# We know this is always true based on the input arguments to `Popen`.
|
||||
stdout: IO[bytes] = process.stdout # type: ignore
|
||||
|
||||
out = stdout.read()
|
||||
stdout.close()
|
||||
print("done!", len(out), "bytes")
|
||||
return out.decode("utf-8", errors="ignore").split("\n")
|
||||
return cast(List[str], out.decode("utf-8", errors="ignore").split("\n"))
|
||||
|
||||
|
||||
def build_info(use_c=True, use_cxx=True, ignore_prefix_list=None):
|
||||
|
||||
def build_info(
|
||||
use_c: bool = True,
|
||||
use_cxx: bool = True,
|
||||
ignore_prefix_list: Optional[List[str]] = None,
|
||||
) -> List[Tuple[str, List[str], List[str]]]:
|
||||
makelog = makefile_log()
|
||||
|
||||
source = []
|
||||
|
||||
compilers = []
|
||||
if use_c:
|
||||
compilers.append(cmake_cache_var("CMAKE_C_COMPILER"))
|
||||
compilers.append(cmake_cache_var_or_exit("CMAKE_C_COMPILER"))
|
||||
if use_cxx:
|
||||
compilers.append(cmake_cache_var("CMAKE_CXX_COMPILER"))
|
||||
compilers.append(cmake_cache_var_or_exit("CMAKE_CXX_COMPILER"))
|
||||
|
||||
print("compilers:", " ".join(compilers))
|
||||
|
||||
|
@ -131,7 +166,7 @@ def build_info(use_c=True, use_cxx=True, ignore_prefix_list=None):
|
|||
|
||||
for line in makelog:
|
||||
|
||||
args = line.split()
|
||||
args: Union[str, List[str]] = line.split()
|
||||
|
||||
if not any([(c in args) for c in compilers]):
|
||||
continue
|
||||
|
@ -176,29 +211,40 @@ def build_info(use_c=True, use_cxx=True, ignore_prefix_list=None):
|
|||
return source
|
||||
|
||||
|
||||
def build_defines_as_source():
|
||||
def build_defines_as_source() -> str:
|
||||
"""
|
||||
Returns a string formatted as an include:
|
||||
'#defines A=B\n#define....'
|
||||
"""
|
||||
import subprocess
|
||||
# works for both gcc and clang
|
||||
cmd = (cmake_cache_var("CMAKE_C_COMPILER"), "-dM", "-E", "-")
|
||||
return subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.DEVNULL,
|
||||
).stdout.read().strip().decode('ascii')
|
||||
cmd = (cmake_cache_var_or_exit("CMAKE_C_COMPILER"), "-dM", "-E", "-")
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
# We know this is always true based on the input arguments to `Popen`.
|
||||
stdout: IO[bytes] = process.stdout # type: ignore
|
||||
|
||||
return cast(str, stdout.read().strip().decode('ascii'))
|
||||
|
||||
|
||||
def build_defines_as_args():
|
||||
return [("-D" + "=".join(l.split(maxsplit=2)[1:]))
|
||||
for l in build_defines_as_source().split("\n")
|
||||
if l.startswith('#define')]
|
||||
def build_defines_as_args() -> List[str]:
|
||||
return [
|
||||
("-D" + "=".join(l.split(maxsplit=2)[1:]))
|
||||
for l in build_defines_as_source().split("\n")
|
||||
if l.startswith('#define')
|
||||
]
|
||||
|
||||
|
||||
# could be moved elsewhere!, this just happens to be used by scripts that also
|
||||
# use this module.
|
||||
def queue_processes(process_funcs, job_total=-1):
|
||||
def queue_processes(
|
||||
process_funcs: Sequence[Tuple[Callable[..., subprocess.Popen[Any]], Tuple[Any, ...]]],
|
||||
job_total: int =-1,
|
||||
) -> None:
|
||||
""" Takes a list of function arg pairs, each function must return a process
|
||||
"""
|
||||
|
||||
|
@ -217,7 +263,7 @@ def queue_processes(process_funcs, job_total=-1):
|
|||
else:
|
||||
import time
|
||||
|
||||
processes = []
|
||||
processes: List[subprocess.Popen[Any]] = []
|
||||
for func, args in process_funcs:
|
||||
# wait until a thread is free
|
||||
while 1:
|
||||
|
@ -234,7 +280,7 @@ def queue_processes(process_funcs, job_total=-1):
|
|||
processes.append(func(*args))
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
if not os.path.exists(join(CMAKE_DIR, "CMakeCache.txt")):
|
||||
print("This script must run from the cmake build dir")
|
||||
return
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Iterable, TextIO
|
||||
from typing import Iterable, TextIO, Optional, Any, Union
|
||||
|
||||
# This script can run from any location,
|
||||
# output is created in the $CWD
|
||||
|
@ -18,21 +19,43 @@ SKIP_NAMES = {
|
|||
".gitignore",
|
||||
".gitmodules",
|
||||
".arcconfig",
|
||||
".svn",
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
output_dir = Path(".").absolute()
|
||||
blender_srcdir = Path(__file__).absolute().parent.parent.parent
|
||||
|
||||
cli_parser = argparse.ArgumentParser(
|
||||
description=f"Create a tarball of the Blender sources, optionally including sources of dependencies.",
|
||||
epilog="This script is intended to be run by `make source_archive_complete`.",
|
||||
)
|
||||
cli_parser.add_argument(
|
||||
"-p",
|
||||
"--include-packages",
|
||||
type=Path,
|
||||
default=None,
|
||||
metavar="PACKAGE_PATH",
|
||||
help="Include all source files from the given package directory as well.",
|
||||
)
|
||||
|
||||
cli_args = cli_parser.parse_args()
|
||||
|
||||
print(f"Source dir: {blender_srcdir}")
|
||||
|
||||
version = parse_blender_version(blender_srcdir)
|
||||
manifest = output_dir / f"blender-{version}-manifest.txt"
|
||||
tarball = output_dir / f"blender-{version}.tar.xz"
|
||||
curdir = blender_srcdir.parent
|
||||
os.chdir(curdir)
|
||||
blender_srcdir = blender_srcdir.relative_to(curdir)
|
||||
|
||||
os.chdir(blender_srcdir)
|
||||
create_manifest(version, manifest)
|
||||
create_tarball(version, tarball, manifest)
|
||||
print(f"Output dir: {curdir}")
|
||||
|
||||
version = parse_blender_version(blender_srcdir)
|
||||
tarball = tarball_path(curdir, version, cli_args)
|
||||
manifest = manifest_path(tarball)
|
||||
packages_dir = packages_path(curdir, cli_args)
|
||||
|
||||
create_manifest(version, manifest, blender_srcdir, packages_dir)
|
||||
create_tarball(version, tarball, manifest, blender_srcdir, packages_dir)
|
||||
create_checksum_file(tarball)
|
||||
cleanup(manifest)
|
||||
print("Done!")
|
||||
|
@ -84,43 +107,109 @@ def parse_blender_version(blender_srcdir: Path) -> BlenderVersion:
|
|||
)
|
||||
|
||||
|
||||
def tarball_path(output_dir: Path, version: BlenderVersion, cli_args: Any) -> Path:
|
||||
extra = ""
|
||||
if cli_args.include_packages:
|
||||
extra = "-with-libraries"
|
||||
|
||||
return output_dir / f"blender{extra}-{version}.tar.xz"
|
||||
|
||||
|
||||
def manifest_path(tarball: Path) -> Path:
|
||||
"""Return the manifest path for the given tarball path.
|
||||
|
||||
>>> from pathlib import Path
|
||||
>>> tarball = Path("/home/sybren/workspace/blender-git/blender-test.tar.gz")
|
||||
>>> manifest_path(tarball).as_posix()
|
||||
'/home/sybren/workspace/blender-git/blender-test-manifest.txt'
|
||||
"""
|
||||
# ".tar.gz" is seen as two suffixes.
|
||||
without_suffix = tarball.with_suffix("").with_suffix("")
|
||||
name = without_suffix.name
|
||||
return without_suffix.with_name(f"{name}-manifest.txt")
|
||||
|
||||
|
||||
def packages_path(current_directory: Path, cli_args: Any) -> Optional[Path]:
|
||||
if not cli_args.include_packages:
|
||||
return None
|
||||
|
||||
abspath = cli_args.include_packages.absolute()
|
||||
|
||||
# os.path.relpath() can return paths like "../../packages", where
|
||||
# Path.relative_to() will not go up directories (so its return value never
|
||||
# has "../" in there).
|
||||
relpath = os.path.relpath(abspath, current_directory)
|
||||
|
||||
return Path(relpath)
|
||||
|
||||
|
||||
### Manifest creation
|
||||
|
||||
|
||||
def create_manifest(version: BlenderVersion, outpath: Path) -> None:
|
||||
def create_manifest(
|
||||
version: BlenderVersion,
|
||||
outpath: Path,
|
||||
blender_srcdir: Path,
|
||||
packages_dir: Optional[Path],
|
||||
) -> None:
|
||||
print(f'Building manifest of files: "{outpath}"...', end="", flush=True)
|
||||
with outpath.open("w", encoding="utf-8") as outfile:
|
||||
main_files_to_manifest(outfile)
|
||||
submodules_to_manifest(version, outfile)
|
||||
main_files_to_manifest(blender_srcdir, outfile)
|
||||
submodules_to_manifest(blender_srcdir, version, outfile)
|
||||
|
||||
if packages_dir:
|
||||
packages_to_manifest(outfile, packages_dir)
|
||||
print("OK")
|
||||
|
||||
|
||||
def main_files_to_manifest(outfile: TextIO) -> None:
|
||||
for path in git_ls_files():
|
||||
def main_files_to_manifest(blender_srcdir: Path, outfile: TextIO) -> None:
|
||||
assert not blender_srcdir.is_absolute()
|
||||
for path in git_ls_files(blender_srcdir):
|
||||
print(path, file=outfile)
|
||||
|
||||
|
||||
def submodules_to_manifest(version: BlenderVersion, outfile: TextIO) -> None:
|
||||
def submodules_to_manifest(
|
||||
blender_srcdir: Path, version: BlenderVersion, outfile: TextIO
|
||||
) -> None:
|
||||
skip_addon_contrib = version.is_release
|
||||
assert not blender_srcdir.is_absolute()
|
||||
|
||||
for line in git_command("submodule"):
|
||||
for line in git_command("-C", blender_srcdir, "submodule"):
|
||||
submodule = line.split()[1]
|
||||
|
||||
# Don't use native slashes as GIT for MS-Windows outputs forward slashes.
|
||||
if skip_addon_contrib and submodule == "release/scripts/addons_contrib":
|
||||
continue
|
||||
|
||||
for path in git_ls_files(Path(submodule)):
|
||||
for path in git_ls_files(blender_srcdir / submodule):
|
||||
print(path, file=outfile)
|
||||
|
||||
|
||||
def create_tarball(version: BlenderVersion, tarball: Path, manifest: Path) -> None:
|
||||
def packages_to_manifest(outfile: TextIO, packages_dir: Path) -> None:
|
||||
for path in packages_dir.glob("*"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
if path.name in SKIP_NAMES:
|
||||
continue
|
||||
print(path, file=outfile)
|
||||
|
||||
|
||||
### Higher-level functions
|
||||
|
||||
|
||||
def create_tarball(
|
||||
version: BlenderVersion, tarball: Path, manifest: Path, blender_srcdir: Path, packages_dir: Optional[Path]
|
||||
) -> None:
|
||||
print(f'Creating archive: "{tarball}" ...', end="", flush=True)
|
||||
command = ["tar"]
|
||||
|
||||
# Requires GNU `tar`, since `--transform` is used.
|
||||
command = [
|
||||
"tar",
|
||||
if packages_dir:
|
||||
command += ["--transform", f"s,{packages_dir}/,packages/,g"]
|
||||
|
||||
command += [
|
||||
"--transform",
|
||||
f"s,^,blender-{version}/,g",
|
||||
f"s,^{blender_srcdir.name}/,blender-{version}/,g",
|
||||
"--use-compress-program=xz -9",
|
||||
"--create",
|
||||
f"--file={tarball}",
|
||||
|
@ -130,7 +219,8 @@ def create_tarball(version: BlenderVersion, tarball: Path, manifest: Path) -> No
|
|||
"--owner=0",
|
||||
"--group=0",
|
||||
]
|
||||
subprocess.run(command, check=True, timeout=300)
|
||||
|
||||
subprocess.run(command, check=True, timeout=3600)
|
||||
print("OK")
|
||||
|
||||
|
||||
|
@ -174,7 +264,7 @@ def git_ls_files(directory: Path = Path(".")) -> Iterable[Path]:
|
|||
yield path
|
||||
|
||||
|
||||
def git_command(*cli_args) -> Iterable[str]:
|
||||
def git_command(*cli_args: Union[bytes, str, Path] ) -> Iterable[str]:
|
||||
"""Generator, yields lines of output from a Git command."""
|
||||
command = ("git", *cli_args)
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ PROJECT_NAME = Blender
|
|||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "V2.93"
|
||||
PROJECT_NUMBER = "V3.0"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
|
|
@ -498,7 +498,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
|
|||
|
||||
/* Compute render passes and film settings. */
|
||||
vector<Pass> passes = sync->sync_render_passes(
|
||||
b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
|
||||
b_scene, b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
|
||||
|
||||
/* Set buffer params, using film settings from sync_render_passes. */
|
||||
buffer_params.passes = passes;
|
||||
|
|
|
@ -569,7 +569,8 @@ int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass)
|
|||
return -1;
|
||||
}
|
||||
|
||||
vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
|
||||
vector<Pass> BlenderSync::sync_render_passes(BL::Scene &b_scene,
|
||||
BL::RenderLayer &b_rlay,
|
||||
BL::ViewLayer &b_view_layer,
|
||||
bool adaptive_sampling,
|
||||
const DenoiseParams &denoising)
|
||||
|
@ -580,7 +581,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
|
|||
for (BL::RenderPass &b_pass : b_rlay.passes) {
|
||||
PassType pass_type = get_pass_type(b_pass);
|
||||
|
||||
if (pass_type == PASS_MOTION && scene->integrator->get_motion_blur())
|
||||
if (pass_type == PASS_MOTION && b_scene.render().use_motion_blur())
|
||||
continue;
|
||||
if (pass_type != PASS_NONE)
|
||||
Pass::add(pass_type, passes, b_pass.name().c_str());
|
||||
|
|
|
@ -74,7 +74,8 @@ class BlenderSync {
|
|||
int height,
|
||||
void **python_thread_state);
|
||||
void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer);
|
||||
vector<Pass> sync_render_passes(BL::RenderLayer &b_render_layer,
|
||||
vector<Pass> sync_render_passes(BL::Scene &b_scene,
|
||||
BL::RenderLayer &b_render_layer,
|
||||
BL::ViewLayer &b_view_layer,
|
||||
bool adaptive_sampling,
|
||||
const DenoiseParams &denoising);
|
||||
|
|
|
@ -362,7 +362,7 @@ class OptiXDevice : public CUDADevice {
|
|||
}
|
||||
}
|
||||
|
||||
OptixModuleCompileOptions module_options;
|
||||
OptixModuleCompileOptions module_options = {};
|
||||
module_options.maxRegisterCount = 0; // Do not set an explicit register limit
|
||||
# ifdef WITH_CYCLES_DEBUG
|
||||
module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0;
|
||||
|
@ -377,7 +377,7 @@ class OptiXDevice : public CUDADevice {
|
|||
module_options.numBoundValues = 0;
|
||||
# endif
|
||||
|
||||
OptixPipelineCompileOptions pipeline_options;
|
||||
OptixPipelineCompileOptions pipeline_options = {};
|
||||
// Default to no motion blur and two-level graph, since it is the fastest option
|
||||
pipeline_options.usesMotionBlur = false;
|
||||
pipeline_options.traversableGraphFlags =
|
||||
|
@ -477,7 +477,7 @@ class OptiXDevice : public CUDADevice {
|
|||
|
||||
# if OPTIX_ABI_VERSION >= 36
|
||||
if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) {
|
||||
OptixBuiltinISOptions builtin_options;
|
||||
OptixBuiltinISOptions builtin_options = {};
|
||||
builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE;
|
||||
builtin_options.usesMotionBlur = false;
|
||||
|
||||
|
@ -571,7 +571,7 @@ class OptiXDevice : public CUDADevice {
|
|||
stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH);
|
||||
# endif
|
||||
|
||||
OptixPipelineLinkOptions link_options;
|
||||
OptixPipelineLinkOptions link_options = {};
|
||||
link_options.maxTraceDepth = 1;
|
||||
# ifdef WITH_CYCLES_DEBUG
|
||||
link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
|
||||
|
@ -953,7 +953,7 @@ class OptiXDevice : public CUDADevice {
|
|||
}
|
||||
|
||||
// Create OptiX denoiser handle on demand when it is first used
|
||||
OptixDenoiserOptions denoiser_options;
|
||||
OptixDenoiserOptions denoiser_options = {};
|
||||
assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3);
|
||||
denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>(
|
||||
OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1));
|
||||
|
@ -1157,7 +1157,7 @@ class OptiXDevice : public CUDADevice {
|
|||
|
||||
// Compute memory usage
|
||||
OptixAccelBufferSizes sizes = {};
|
||||
OptixAccelBuildOptions options;
|
||||
OptixAccelBuildOptions options = {};
|
||||
options.operation = operation;
|
||||
if (background) {
|
||||
// Prefer best performance and lowest memory consumption in background
|
||||
|
@ -1195,7 +1195,7 @@ class OptiXDevice : public CUDADevice {
|
|||
}
|
||||
|
||||
// Finally build the acceleration structure
|
||||
OptixAccelEmitDesc compacted_size_prop;
|
||||
OptixAccelEmitDesc compacted_size_prop = {};
|
||||
compacted_size_prop.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE;
|
||||
// A tiny space was allocated for this property at the end of the temporary buffer above
|
||||
// Make sure this pointer is 8-byte aligned
|
||||
|
|
|
@ -135,6 +135,8 @@ ccl_device_inline bool lamp_light_sample(
|
|||
ls->pdf = invarea;
|
||||
}
|
||||
else {
|
||||
inplane = ls->P;
|
||||
|
||||
float3 sample_axisu = axisu;
|
||||
float3 sample_axisv = axisv;
|
||||
|
||||
|
@ -145,7 +147,6 @@ ccl_device_inline bool lamp_light_sample(
|
|||
}
|
||||
}
|
||||
|
||||
inplane = ls->P;
|
||||
ls->pdf = rect_light_sample(P, &ls->P, sample_axisu, sample_axisv, randu, randv, true);
|
||||
inplane = ls->P - inplane;
|
||||
}
|
||||
|
|
|
@ -1364,7 +1364,6 @@ void GeometryManager::device_update_bvh(Device *device,
|
|||
}
|
||||
|
||||
dscene->data.bvh.root = pack.root_index;
|
||||
dscene->data.bvh.bvh_layout = bparams.bvh_layout;
|
||||
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
|
||||
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
|
||||
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
|
||||
|
@ -1984,6 +1983,11 @@ void GeometryManager::device_update(Device *device,
|
|||
}
|
||||
}
|
||||
|
||||
/* Always set BVH layout again after displacement where it was set to none,
|
||||
* to avoid ray-tracing at that stage. */
|
||||
dscene->data.bvh.bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
|
||||
device->get_bvh_layout_mask());
|
||||
|
||||
{
|
||||
scoped_callback_timer timer([scene](double time) {
|
||||
if (scene->update_stats) {
|
||||
|
|
|
@ -1442,12 +1442,23 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
|||
*/
|
||||
break;
|
||||
case WM_SYSCOMMAND:
|
||||
/* The WM_SYSCHAR message is sent to the window when system commands such as
|
||||
/* The WM_SYSCOMMAND message is sent to the window when system commands such as
|
||||
* maximize, minimize or close the window are triggered. Also it is sent when ALT
|
||||
* button is press for menu. To prevent this we must return preventing DefWindowProc.
|
||||
*
|
||||
* Note that the four low-order bits of the wParam parameter are used internally by the
|
||||
* OS. To obtain the correct result when testing the value of wParam, an application
|
||||
* must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.
|
||||
*/
|
||||
if (wParam == SC_KEYMENU) {
|
||||
eventHandled = true;
|
||||
switch (wParam & 0xFFF0) {
|
||||
case SC_KEYMENU:
|
||||
eventHandled = true;
|
||||
break;
|
||||
case SC_RESTORE:
|
||||
::ShowWindow(hwnd, SW_RESTORE);
|
||||
window->setState(window->getState());
|
||||
eventHandled = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -521,7 +521,7 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
|
|||
|
||||
switch (state) {
|
||||
case GHOST_kWindowStateMinimized:
|
||||
wp.showCmd = SW_SHOWMINIMIZED;
|
||||
wp.showCmd = SW_MINIMIZE;
|
||||
break;
|
||||
case GHOST_kWindowStateMaximized:
|
||||
wp.showCmd = SW_SHOWMAXIMIZED;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2cef4877edc40875978c4e95322bb5193f5815bf
|
||||
Subproject commit f7b706dd6434db2d752f47c4b8c3148b2990fd73
|
Binary file not shown.
|
@ -1 +1 @@
|
|||
Subproject commit bcd08a9506d33bdd7358201031b04d041ef22d94
|
||||
Subproject commit 81815ea92c2071a08566dc66d4a871b6e2f5c868
|
|
@ -1 +1 @@
|
|||
Subproject commit f948f658ba33eb670a65e0bba058d43138abea7e
|
||||
Subproject commit 8970953d4a8a4ea3bf77c66370c817ed0cf1308a
|
|
@ -4469,8 +4469,9 @@ def km_sculpt(params):
|
|||
)
|
||||
|
||||
items.extend([
|
||||
# Switch Object (release to avoid conflict with grease pencil drawing).
|
||||
("object.switch_object", {"type": 'D', "value": 'RELEASE'}, None),
|
||||
# Transfer Sculpt Mode (release to avoid conflict with grease pencil drawing).
|
||||
("object.transfer_mode", {"type": 'D', "value": 'RELEASE'},
|
||||
{"properties": [("use_eyedropper", False), ("flash_object", True)]}),
|
||||
# Brush strokes
|
||||
("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [("mode", 'NORMAL')]}),
|
||||
|
@ -4595,8 +4596,6 @@ def km_mesh(params):
|
|||
)
|
||||
|
||||
items.extend([
|
||||
# Switch Object (release to avoid conflict with grease pencil drawing).
|
||||
("object.switch_object", {"type": 'D', "value": 'RELEASE'}, None),
|
||||
# Tools.
|
||||
("mesh.loopcut_slide", {"type": 'R', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False)],)]}),
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
import os
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty
|
||||
from mathutils import (
|
||||
|
@ -356,6 +355,7 @@ class CLIP_OT_delete_proxy(Operator):
|
|||
|
||||
@staticmethod
|
||||
def _rmproxy(abspath):
|
||||
import os
|
||||
import shutil
|
||||
|
||||
if not os.path.exists(abspath):
|
||||
|
@ -367,6 +367,7 @@ class CLIP_OT_delete_proxy(Operator):
|
|||
os.remove(abspath)
|
||||
|
||||
def execute(self, context):
|
||||
import os
|
||||
sc = context.space_data
|
||||
clip = sc.clip
|
||||
if clip.use_proxy_custom_directory:
|
||||
|
|
|
@ -306,6 +306,62 @@ class NODE_OT_tree_path_parent(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class NODE_OT_active_preview_toggle(Operator):
|
||||
'''Toggle active preview state of node'''
|
||||
bl_idname = "node.active_preview_toggle"
|
||||
bl_label = "Toggle Active Preview"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
if space.type != 'NODE_EDITOR':
|
||||
return False
|
||||
if space.edit_tree is None:
|
||||
return False
|
||||
if space.edit_tree.nodes.active is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
node_editor = context.space_data
|
||||
ntree = node_editor.edit_tree
|
||||
active_node = ntree.nodes.active
|
||||
|
||||
if active_node.active_preview:
|
||||
self.disable_preview(context, ntree, active_node)
|
||||
else:
|
||||
self.enable_preview(context, node_editor, ntree, active_node)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def enable_preview(self, context, node_editor, ntree, active_node):
|
||||
spreadsheets = self.find_unpinned_spreadsheets(context)
|
||||
|
||||
for spreadsheet in spreadsheets:
|
||||
spreadsheet.set_geometry_node_context(node_editor, active_node)
|
||||
|
||||
for node in ntree.nodes:
|
||||
node.active_preview = False
|
||||
active_node.active_preview = True
|
||||
|
||||
def disable_preview(self, context, ntree, active_node):
|
||||
spreadsheets = self.find_unpinned_spreadsheets(context)
|
||||
for spreadsheet in spreadsheets:
|
||||
spreadsheet.context_path.clear()
|
||||
|
||||
active_node.active_preview = False
|
||||
|
||||
def find_unpinned_spreadsheets(self, context):
|
||||
spreadsheets = []
|
||||
for window in context.window_manager.windows:
|
||||
for area in window.screen.areas:
|
||||
space = area.spaces.active
|
||||
if space.type == 'SPREADSHEET' and not space.is_pinned:
|
||||
spreadsheets.append(space)
|
||||
return spreadsheets
|
||||
|
||||
|
||||
classes = (
|
||||
NodeSetting,
|
||||
|
||||
|
@ -314,4 +370,5 @@ classes = (
|
|||
NODE_OT_add_search,
|
||||
NODE_OT_collapse_hide_unused_toggle,
|
||||
NODE_OT_tree_path_parent,
|
||||
NODE_OT_active_preview_toggle,
|
||||
)
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
import os
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
@ -62,6 +61,7 @@ class PlayRenderedAnim(Operator):
|
|||
bl_options = {'REGISTER'}
|
||||
|
||||
def execute(self, context):
|
||||
import os
|
||||
import subprocess
|
||||
from shlex import quote
|
||||
|
||||
|
|
|
@ -34,13 +34,45 @@ class SPREADSHEET_OT_toggle_pin(Operator):
|
|||
def execute(self, context):
|
||||
space = context.space_data
|
||||
|
||||
if space.pinned_id:
|
||||
space.pinned_id = None
|
||||
if space.is_pinned:
|
||||
self.unpin(context)
|
||||
else:
|
||||
space.pinned_id = context.active_object
|
||||
|
||||
self.pin(context)
|
||||
return {'FINISHED'}
|
||||
|
||||
def pin(self, context):
|
||||
space = context.space_data
|
||||
space.is_pinned = True
|
||||
|
||||
def unpin(self, context):
|
||||
space = context.space_data
|
||||
space.is_pinned = False
|
||||
|
||||
space.context_path.clear()
|
||||
|
||||
# Try to find a node with an active preview in any open editor.
|
||||
if space.object_eval_state == 'EVALUATED':
|
||||
node_editors = self.find_geometry_node_editors(context)
|
||||
for node_editor in node_editors:
|
||||
ntree = node_editor.edit_tree
|
||||
for node in ntree.nodes:
|
||||
if node.active_preview:
|
||||
space.set_geometry_node_context(node_editor, node)
|
||||
return
|
||||
|
||||
def find_geometry_node_editors(self, context):
|
||||
editors = []
|
||||
for window in context.window_manager.windows:
|
||||
for area in window.screen.areas:
|
||||
space = area.spaces.active
|
||||
if space.type != 'NODE_EDITOR':
|
||||
continue
|
||||
if space.edit_tree is None:
|
||||
continue
|
||||
if space.edit_tree.type == 'GEOMETRY':
|
||||
editors.append(space)
|
||||
return editors
|
||||
|
||||
|
||||
classes = (
|
||||
SPREADSHEET_OT_toggle_pin,
|
||||
|
|
|
@ -71,10 +71,10 @@ class POINTCLOUD_MT_add_attribute(Menu):
|
|||
layout = self.layout
|
||||
pointcloud = context.pointcloud
|
||||
|
||||
self.add_standard_attribute(layout, pointcloud, 'Radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'Color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'Particle ID', 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'Velocity', 'FLOAT_VECTOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'id', 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'velocity', 'FLOAT_VECTOR', 'POINT')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
|
|
@ -147,8 +147,7 @@ class GreasePencilDisplayPanel:
|
|||
|
||||
if self.is_popover:
|
||||
row = layout.row(align=True)
|
||||
row.prop(settings, "show_brush", text="")
|
||||
row.label(text="Display Cursor")
|
||||
row.prop(settings, "show_brush", text="Display Cursor")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.active = settings.show_brush
|
||||
|
|
|
@ -28,8 +28,17 @@ class SPREADSHEET_HT_header(bpy.types.Header):
|
|||
|
||||
layout.template_header()
|
||||
|
||||
pinned_id = space.pinned_id
|
||||
used_id = pinned_id if pinned_id else context.active_object
|
||||
if len(space.context_path) == 0:
|
||||
self.draw_without_context_path(layout)
|
||||
return
|
||||
root_context = space.context_path[0]
|
||||
if root_context.type != 'OBJECT':
|
||||
self.draw_without_context_path(layout)
|
||||
return
|
||||
obj = root_context.object
|
||||
if obj is None:
|
||||
self.draw_without_context_path(layout)
|
||||
return
|
||||
|
||||
layout.prop(space, "object_eval_state", text="")
|
||||
if space.object_eval_state != 'ORIGINAL':
|
||||
|
@ -37,16 +46,61 @@ class SPREADSHEET_HT_header(bpy.types.Header):
|
|||
if space.geometry_component_type != 'INSTANCES':
|
||||
layout.prop(space, "attribute_domain", text="")
|
||||
|
||||
if used_id:
|
||||
layout.label(text=used_id.name, icon='OBJECT_DATA')
|
||||
context_path = space.context_path
|
||||
if space.object_eval_state == 'ORIGINAL':
|
||||
# Only show first context.
|
||||
context_path = context_path[:1]
|
||||
if space.display_context_path_collapsed:
|
||||
self.draw_collapsed_context_path(context, layout, context_path)
|
||||
else:
|
||||
self.draw_full_context_path(context, layout, context_path)
|
||||
|
||||
layout.operator("spreadsheet.toggle_pin", text="", icon='PINNED' if pinned_id else 'UNPINNED', emboss=False)
|
||||
pin_icon = 'PINNED' if space.is_pinned else 'UNPINNED'
|
||||
layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False)
|
||||
|
||||
layout.separator_spacer()
|
||||
|
||||
if isinstance(used_id, bpy.types.Object) and used_id.mode == 'EDIT':
|
||||
if isinstance(obj, bpy.types.Object) and obj.mode == 'EDIT':
|
||||
layout.prop(space, "show_only_selected", text="Selected Only")
|
||||
|
||||
def draw_without_context_path(self, layout):
|
||||
layout.label(text="No active context")
|
||||
|
||||
def draw_full_context_path(self, context, layout, context_path):
|
||||
space = context.space_data
|
||||
row = layout.row()
|
||||
for ctx in context_path[:-1]:
|
||||
subrow = row.row(align=True)
|
||||
self.draw_spreadsheet_context(subrow, ctx)
|
||||
self.draw_spreadsheet_context_path_icon(subrow, space)
|
||||
|
||||
self.draw_spreadsheet_context(row, context_path[-1])
|
||||
|
||||
def draw_collapsed_context_path(self, context, layout, context_path):
|
||||
space = context.space_data
|
||||
row = layout.row(align=True)
|
||||
self.draw_spreadsheet_context(row, context_path[0])
|
||||
if len(context_path) == 1:
|
||||
return
|
||||
self.draw_spreadsheet_context_path_icon(row, space)
|
||||
if len(context_path) > 2:
|
||||
self.draw_spreadsheet_context_path_icon(row, space, icon='DOT')
|
||||
self.draw_spreadsheet_context_path_icon(row, space)
|
||||
self.draw_spreadsheet_context(row, context_path[-1])
|
||||
|
||||
def draw_spreadsheet_context(self, layout, ctx):
|
||||
if ctx.type == 'OBJECT':
|
||||
if ctx.object is None:
|
||||
layout.label(text="<no object>", icon='OBJECT_DATA')
|
||||
else:
|
||||
layout.label(text=ctx.object.name, icon='OBJECT_DATA')
|
||||
elif ctx.type == 'MODIFIER':
|
||||
layout.label(text=ctx.modifier_name, icon='MODIFIER')
|
||||
elif ctx.type == 'NODE':
|
||||
layout.label(text=ctx.node_name, icon='NODE')
|
||||
|
||||
def draw_spreadsheet_context_path_icon(self, layout, space, icon='RIGHTARROW_THIN'):
|
||||
layout.prop(space, "display_context_path_collapsed", icon_only=True, emboss=False, icon=icon)
|
||||
|
||||
classes = (
|
||||
SPREADSHEET_HT_header,
|
||||
|
|
|
@ -2241,7 +2241,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
|||
self._draw_items(
|
||||
context, (
|
||||
({"property": "use_sculpt_vertex_colors"}, "T71947"),
|
||||
({"property": "use_switch_object_operator"}, "T80402"),
|
||||
({"property": "use_sculpt_tools_tilt"}, "T82877"),
|
||||
({"property": "use_asset_browser"}, ("project/profile/124/", "Milestone 1")),
|
||||
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
|
||||
|
|
|
@ -1366,7 +1366,7 @@ class VIEW3D_MT_select_object(Menu):
|
|||
|
||||
layout.operator_menu_enum("object.select_by_type", "type", text="Select All by Type")
|
||||
layout.operator("object.select_camera", text="Select Active Camera")
|
||||
layout.operator("object.select_mirror", text="Mirror Selection")
|
||||
layout.operator("object.select_mirror")
|
||||
layout.operator("object.select_random", text="Select Random")
|
||||
|
||||
layout.separator()
|
||||
|
@ -1424,7 +1424,7 @@ class VIEW3D_MT_select_pose(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("pose.select_mirror", text="Flip Active")
|
||||
layout.operator("pose.select_mirror")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -1597,7 +1597,7 @@ class VIEW3D_MT_select_edit_mesh(Menu):
|
|||
layout.separator()
|
||||
|
||||
layout.operator("mesh.select_axis", text="Side of Active")
|
||||
layout.operator("mesh.select_mirror", text="Mirror Selection")
|
||||
layout.operator("mesh.select_mirror")
|
||||
|
||||
|
||||
class VIEW3D_MT_select_edit_curve(Menu):
|
||||
|
@ -1784,7 +1784,7 @@ class VIEW3D_MT_select_edit_armature(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("armature.select_mirror", text="Mirror").extend = False
|
||||
layout.operator("armature.select_mirror")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -3040,9 +3040,16 @@ class VIEW3D_MT_sculpt(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
props = layout.operator("object.transfer_mode", text="Transfer Sculpt Mode")
|
||||
props.use_eyedropper = True
|
||||
props.flash_object = False
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("sculpt.reset_brushes")
|
||||
|
||||
|
||||
|
||||
class VIEW3D_MT_mask(Menu):
|
||||
bl_label = "Mask"
|
||||
|
||||
|
@ -3094,19 +3101,15 @@ class VIEW3D_MT_mask(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
props = layout.operator("sculpt.mask_expand", text="Expand Mask by Topology")
|
||||
props.use_normals = False
|
||||
props.keep_previous_mask = False
|
||||
props = layout.operator("sculpt.expand", text="Expand Mask by Topology")
|
||||
props.target = 'MASK'
|
||||
props.falloff_type = 'GEODESIC'
|
||||
props.invert = True
|
||||
props.smooth_iterations = 2
|
||||
props.create_face_set = False
|
||||
|
||||
props = layout.operator("sculpt.mask_expand", text="Expand Mask by Curvature")
|
||||
props.use_normals = True
|
||||
props.keep_previous_mask = True
|
||||
props = layout.operator("sculpt.expand", text="Expand Mask by Normals")
|
||||
props.target = 'MASK'
|
||||
props.falloff_type = 'NORMALS'
|
||||
props.invert = False
|
||||
props.smooth_iterations = 0
|
||||
props.create_face_set = False
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -3160,6 +3163,20 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
props = layout.operator("sculpt.expand", text="Expand Face Set by Topology")
|
||||
props.target = 'FACE_SETS'
|
||||
props.falloff_type = 'GEODESIC'
|
||||
props.invert = False
|
||||
props.use_modify_active = False
|
||||
|
||||
props = layout.operator("sculpt.expand", text="Expand Active Face Set")
|
||||
props.target = 'FACE_SETS'
|
||||
props.falloff_type = 'BOUNDARY_FACE_SET'
|
||||
props.invert = False
|
||||
props.use_modify_active = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("mesh.face_set_extract", text='Extract Face Set')
|
||||
|
||||
layout.separator()
|
||||
|
|
|
@ -31,7 +31,7 @@ extern "C" {
|
|||
*/
|
||||
|
||||
/* Blender major and minor version. */
|
||||
#define BLENDER_VERSION 293
|
||||
#define BLENDER_VERSION 300
|
||||
/* Blender patch version for bugfix releases. */
|
||||
#define BLENDER_VERSION_PATCH 0
|
||||
/** Blender release cycle stage: alpha/beta/rc/release. */
|
||||
|
@ -39,7 +39,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 17
|
||||
#define BLENDER_FILE_SUBVERSION 0
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
|
|
@ -40,7 +40,6 @@ struct MDeformVert;
|
|||
struct Main;
|
||||
struct Nurb;
|
||||
struct Object;
|
||||
struct Path;
|
||||
struct TextBox;
|
||||
struct rctf;
|
||||
|
||||
|
|
|
@ -289,10 +289,22 @@ typedef struct bNodeType {
|
|||
void (*freefunc_api)(struct PointerRNA *ptr);
|
||||
void (*copyfunc_api)(struct PointerRNA *ptr, const struct bNode *src_node);
|
||||
|
||||
/* can this node type be added to a node tree */
|
||||
bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree);
|
||||
/* can this node be added to a node tree */
|
||||
bool (*poll_instance)(struct bNode *node, struct bNodeTree *nodetree);
|
||||
/**
|
||||
* Can this node type be added to a node tree?
|
||||
* \param r_disabled_hint: Optional hint to display in the UI when the poll fails.
|
||||
* The callback can set this to a static string without having to
|
||||
* null-check it (or without setting it to null if it's not used).
|
||||
* The caller must pass a valid `const char **` and null-initialize it
|
||||
* when it's not just a dummy, that is, if it actually wants to access
|
||||
* the returned disabled-hint (null-check needed!).
|
||||
*/
|
||||
bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree, const char **r_disabled_hint);
|
||||
/** Can this node be added to a node tree?
|
||||
* \param r_disabled_hint: See `poll()`.
|
||||
*/
|
||||
bool (*poll_instance)(struct bNode *node,
|
||||
struct bNodeTree *nodetree,
|
||||
const char **r_disabled_hint);
|
||||
|
||||
/* optional handling of link insertion */
|
||||
void (*insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
|
||||
|
@ -804,7 +816,9 @@ void BKE_node_preview_set_pixel(
|
|||
void nodeLabel(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
|
||||
const char *nodeSocketLabel(const struct bNodeSocket *sock);
|
||||
|
||||
int nodeGroupPoll(struct bNodeTree *nodetree, struct bNodeTree *grouptree);
|
||||
bool nodeGroupPoll(struct bNodeTree *nodetree,
|
||||
struct bNodeTree *grouptree,
|
||||
const char **r_disabled_hint);
|
||||
|
||||
/* Init a new node type struct with default values and callbacks */
|
||||
void node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_session_uuid.h"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
|
@ -80,30 +79,37 @@ struct NodeWarning {
|
|||
};
|
||||
|
||||
struct AvailableAttributeInfo {
|
||||
std::string name;
|
||||
AttributeDomain domain;
|
||||
CustomDataType data_type;
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t domain_hash = (uint64_t)domain;
|
||||
uint64_t data_type_hash = (uint64_t)data_type;
|
||||
return (domain_hash * 33) ^ (data_type_hash * 89);
|
||||
return blender::get_default_hash(name);
|
||||
}
|
||||
|
||||
friend bool operator==(const AvailableAttributeInfo &a, const AvailableAttributeInfo &b)
|
||||
{
|
||||
return a.domain == b.domain && a.data_type == b.data_type;
|
||||
return a.name == b.name;
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeUIStorage {
|
||||
blender::Vector<NodeWarning> warnings;
|
||||
blender::MultiValueMap<std::string, AvailableAttributeInfo> attribute_hints;
|
||||
blender::Set<AvailableAttributeInfo> attribute_hints;
|
||||
};
|
||||
|
||||
struct NodeTreeUIStorage {
|
||||
blender::Map<NodeTreeEvaluationContext, blender::Map<std::string, NodeUIStorage>> context_map;
|
||||
std::mutex context_map_mutex;
|
||||
|
||||
/**
|
||||
* Attribute search uses this to store the fake info for the string typed into a node, in order
|
||||
* to pass the info to the execute callback that sets node socket values. This is mutable since
|
||||
* we can count on only one attribute search being open at a time, and there is no real data
|
||||
* stored here.
|
||||
*/
|
||||
mutable AvailableAttributeInfo dummy_info_for_search;
|
||||
};
|
||||
|
||||
const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C,
|
||||
|
|
|
@ -70,7 +70,9 @@ void BKE_object_free_curve_cache(struct Object *ob);
|
|||
void BKE_object_free_derived_caches(struct Object *ob);
|
||||
void BKE_object_free_caches(struct Object *object);
|
||||
|
||||
void BKE_object_set_preview_geometry_set(struct Object *ob, struct GeometrySet *geometry_set);
|
||||
void BKE_object_preview_geometry_set_add(struct Object *ob,
|
||||
const uint64_t key,
|
||||
struct GeometrySet *geometry_set);
|
||||
|
||||
void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd);
|
||||
void BKE_object_modifier_gpencil_hook_reset(struct Object *ob,
|
||||
|
|
|
@ -63,40 +63,40 @@ typedef struct tSplineIK_Tree {
|
|||
|
||||
bPoseChannel *root; /* bone that is the root node of the chain */
|
||||
|
||||
bConstraint *con; /* constraint for this chain */
|
||||
bSplineIKConstraint *ikData; /* constraint settings for this chain */
|
||||
bConstraint *con; /* constraint for this chain */
|
||||
bSplineIKConstraint *ik_data; /* constraint settings for this chain */
|
||||
} tSplineIK_Tree;
|
||||
|
||||
/* ----------- */
|
||||
|
||||
/* Tag the bones in the chain formed by the given bone for IK */
|
||||
/* Tag the bones in the chain formed by the given bone for IK. */
|
||||
static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
|
||||
Object *UNUSED(ob),
|
||||
bPoseChannel *pchan_tip)
|
||||
{
|
||||
bPoseChannel *pchan, *pchanRoot = NULL;
|
||||
bPoseChannel *pchanChain[255];
|
||||
bPoseChannel *pchan, *pchan_root = NULL;
|
||||
bPoseChannel *pchan_chain[255];
|
||||
bConstraint *con = NULL;
|
||||
bSplineIKConstraint *ikData = NULL;
|
||||
float boneLengths[255];
|
||||
float totLength = 0.0f;
|
||||
bSplineIKConstraint *ik_data = NULL;
|
||||
float bone_lengths[255];
|
||||
float totlength = 0.0f;
|
||||
int segcount = 0;
|
||||
|
||||
/* find the SplineIK constraint */
|
||||
/* Find the SplineIK constraint. */
|
||||
for (con = pchan_tip->constraints.first; con; con = con->next) {
|
||||
if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
|
||||
ikData = con->data;
|
||||
ik_data = con->data;
|
||||
|
||||
/* target can only be curve */
|
||||
if ((ikData->tar == NULL) || (ikData->tar->type != OB_CURVE)) {
|
||||
/* Target can only be a curve. */
|
||||
if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVE)) {
|
||||
continue;
|
||||
}
|
||||
/* skip if disabled */
|
||||
/* Skip if disabled. */
|
||||
if ((con->enforce == 0.0f) || (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* otherwise, constraint is ok... */
|
||||
/* Otherwise, constraint is ok... */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -104,102 +104,102 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
|
|||
return;
|
||||
}
|
||||
|
||||
/* find the root bone and the chain of bones from the root to the tip
|
||||
/* Find the root bone and the chain of bones from the root to the tip.
|
||||
* NOTE: this assumes that the bones are connected, but that may not be true... */
|
||||
for (pchan = pchan_tip; pchan && (segcount < ikData->chainlen);
|
||||
for (pchan = pchan_tip; pchan && (segcount < ik_data->chainlen);
|
||||
pchan = pchan->parent, segcount++) {
|
||||
/* store this segment in the chain */
|
||||
pchanChain[segcount] = pchan;
|
||||
/* Store this segment in the chain. */
|
||||
pchan_chain[segcount] = pchan;
|
||||
|
||||
/* if performing rebinding, calculate the length of the bone */
|
||||
boneLengths[segcount] = pchan->bone->length;
|
||||
totLength += boneLengths[segcount];
|
||||
/* If performing rebinding, calculate the length of the bone. */
|
||||
bone_lengths[segcount] = pchan->bone->length;
|
||||
totlength += bone_lengths[segcount];
|
||||
}
|
||||
|
||||
if (segcount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
pchanRoot = pchanChain[segcount - 1];
|
||||
pchan_root = pchan_chain[segcount - 1];
|
||||
|
||||
/* perform binding step if required */
|
||||
if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
|
||||
/* Perform binding step if required. */
|
||||
if ((ik_data->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
|
||||
float segmentLen = (1.0f / (float)segcount);
|
||||
|
||||
/* setup new empty array for the points list */
|
||||
if (ikData->points) {
|
||||
MEM_freeN(ikData->points);
|
||||
/* Setup new empty array for the points list. */
|
||||
if (ik_data->points) {
|
||||
MEM_freeN(ik_data->points);
|
||||
}
|
||||
ikData->numpoints = ikData->chainlen + 1;
|
||||
ikData->points = MEM_mallocN(sizeof(float) * ikData->numpoints, "Spline IK Binding");
|
||||
ik_data->numpoints = ik_data->chainlen + 1;
|
||||
ik_data->points = MEM_mallocN(sizeof(float) * ik_data->numpoints, "Spline IK Binding");
|
||||
|
||||
/* bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint) */
|
||||
ikData->points[0] = 1.0f;
|
||||
/* Bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint). */
|
||||
ik_data->points[0] = 1.0f;
|
||||
|
||||
/* perform binding of the joints to parametric positions along the curve based
|
||||
* proportion of the total length that each bone occupies
|
||||
/* Perform binding of the joints to parametric positions along the curve based
|
||||
* proportion of the total length that each bone occupies.
|
||||
*/
|
||||
for (int i = 0; i < segcount; i++) {
|
||||
/* 'head' joints, traveling towards the root of the chain
|
||||
* - 2 methods; the one chosen depends on whether we've got usable lengths
|
||||
/* 'head' joints, traveling towards the root of the chain.
|
||||
* - 2 methods; the one chosen depends on whether we've got usable lengths.
|
||||
*/
|
||||
if ((ikData->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totLength == 0.0f)) {
|
||||
/* 1) equi-spaced joints */
|
||||
ikData->points[i + 1] = ikData->points[i] - segmentLen;
|
||||
if ((ik_data->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totlength == 0.0f)) {
|
||||
/* 1) Equi-spaced joints. */
|
||||
ik_data->points[i + 1] = ik_data->points[i] - segmentLen;
|
||||
}
|
||||
else {
|
||||
/* 2) to find this point on the curve, we take a step from the previous joint
|
||||
* a distance given by the proportion that this bone takes
|
||||
/* 2) To find this point on the curve, we take a step from the previous joint
|
||||
* a distance given by the proportion that this bone takes.
|
||||
*/
|
||||
ikData->points[i + 1] = ikData->points[i] - (boneLengths[i] / totLength);
|
||||
ik_data->points[i + 1] = ik_data->points[i] - (bone_lengths[i] / totlength);
|
||||
}
|
||||
}
|
||||
|
||||
/* spline has now been bound */
|
||||
ikData->flag |= CONSTRAINT_SPLINEIK_BOUND;
|
||||
/* Spline has now been bound. */
|
||||
ik_data->flag |= CONSTRAINT_SPLINEIK_BOUND;
|
||||
}
|
||||
|
||||
/* disallow negative values (happens with float precision) */
|
||||
CLAMP_MIN(ikData->points[segcount], 0.0f);
|
||||
/* Disallow negative values (happens with float precision). */
|
||||
CLAMP_MIN(ik_data->points[segcount], 0.0f);
|
||||
|
||||
/* make a new Spline-IK chain, and store it in the IK chains */
|
||||
/* Make a new Spline-IK chain, and store it in the IK chains. */
|
||||
/* TODO: we should check if there is already an IK chain on this,
|
||||
* since that would take precedence... */
|
||||
{
|
||||
/* make new tree */
|
||||
/* Make a new tree. */
|
||||
tSplineIK_Tree *tree = MEM_callocN(sizeof(tSplineIK_Tree), "SplineIK Tree");
|
||||
tree->type = CONSTRAINT_TYPE_SPLINEIK;
|
||||
|
||||
tree->chainlen = segcount;
|
||||
tree->totlength = totLength;
|
||||
tree->totlength = totlength;
|
||||
|
||||
/* copy over the array of links to bones in the chain (from tip to root) */
|
||||
/* Copy over the array of links to bones in the chain (from tip to root). */
|
||||
tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain");
|
||||
memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount);
|
||||
memcpy(tree->chain, pchan_chain, sizeof(bPoseChannel *) * segcount);
|
||||
|
||||
/* store reference to joint position array */
|
||||
tree->points = ikData->points;
|
||||
/* Store reference to joint position array. */
|
||||
tree->points = ik_data->points;
|
||||
|
||||
/* store references to different parts of the chain */
|
||||
tree->root = pchanRoot;
|
||||
/* Store references to different parts of the chain. */
|
||||
tree->root = pchan_root;
|
||||
tree->con = con;
|
||||
tree->ikData = ikData;
|
||||
tree->ik_data = ik_data;
|
||||
|
||||
/* AND! link the tree to the root */
|
||||
BLI_addtail(&pchanRoot->siktree, tree);
|
||||
/* AND! Link the tree to the root. */
|
||||
BLI_addtail(&pchan_root->siktree, tree);
|
||||
}
|
||||
|
||||
/* mark root channel having an IK tree */
|
||||
pchanRoot->flag |= POSE_IKSPLINE;
|
||||
/* Mark root channel having an IK tree. */
|
||||
pchan_root->flag |= POSE_IKSPLINE;
|
||||
}
|
||||
|
||||
/* Tag which bones are members of Spline IK chains */
|
||||
/* Tag which bones are members of Spline IK chains. */
|
||||
static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime))
|
||||
{
|
||||
bPoseChannel *pchan;
|
||||
|
||||
/* find the tips of Spline IK chains,
|
||||
* which are simply the bones which have been tagged as such */
|
||||
/* Find the tips of Spline IK chains,
|
||||
* which are simply the bones which have been tagged as such. */
|
||||
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if (pchan->constflag & PCHAN_HAS_SPLINEIK) {
|
||||
splineik_init_tree_from_pchan(scene, ob, pchan);
|
||||
|
@ -213,21 +213,24 @@ typedef struct tSplineIk_EvalState {
|
|||
float curve_position; /* Current position along the curve. */
|
||||
float curve_scale; /* Global scale to apply to curve positions. */
|
||||
float locrot_offset[4][4]; /* Bone rotation and location offset inherited from parent. */
|
||||
float prev_tail_loc[3]; /* Tail location of the previous bone. */
|
||||
float prev_tail_radius; /* Tail curve radius of the previous bone. */
|
||||
int prev_tail_seg_idx; /* Curve segment the previous tail bone belongs to. */
|
||||
} tSplineIk_EvalState;
|
||||
|
||||
/* Prepare data to evaluate spline IK. */
|
||||
static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *state)
|
||||
{
|
||||
bSplineIKConstraint *ikData = tree->ikData;
|
||||
bSplineIKConstraint *ik_data = tree->ik_data;
|
||||
|
||||
/* Make sure that the constraint targets are ok, to avoid crashes
|
||||
* in case of a depsgraph bug or dependency cycle.
|
||||
*/
|
||||
if (ikData->tar == NULL) {
|
||||
if (ik_data->tar == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CurveCache *cache = ikData->tar->runtime.curve_cache;
|
||||
CurveCache *cache = ik_data->tar->runtime.curve_cache;
|
||||
|
||||
if (ELEM(NULL, cache, cache->anim_path_accum_length)) {
|
||||
return false;
|
||||
|
@ -237,97 +240,248 @@ static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *st
|
|||
state->curve_position = 0.0f;
|
||||
state->curve_scale = 1.0f;
|
||||
unit_m4(state->locrot_offset);
|
||||
zero_v3(state->prev_tail_loc);
|
||||
state->prev_tail_radius = 1.0f;
|
||||
state->prev_tail_seg_idx = 0;
|
||||
|
||||
/* Apply corrections for sensitivity to scaling. */
|
||||
if ((ikData->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
|
||||
/* get the current length of the curve */
|
||||
/* NOTE: this is assumed to be correct even after the curve was resized */
|
||||
const float splineLen = BKE_anim_path_get_length(cache);
|
||||
if ((ik_data->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
|
||||
/* Get the current length of the curve. */
|
||||
/* NOTE: This is assumed to be correct even after the curve was resized. */
|
||||
const float spline_len = BKE_anim_path_get_length(cache);
|
||||
|
||||
/* calculate the scale factor to multiply all the path values by so that the
|
||||
* bone chain retains its current length, such that
|
||||
/* Calculate the scale factor to multiply all the path values by so that the
|
||||
* bone chain retains its current length, such that:
|
||||
* maxScale * splineLen = totLength
|
||||
*/
|
||||
state->curve_scale = tree->totlength / splineLen;
|
||||
state->curve_scale = tree->totlength / spline_len;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void apply_curve_transform(
|
||||
bSplineIKConstraint *ik_data, Object *ob, float radius, float r_vec[3], float *r_radius)
|
||||
{
|
||||
/* Apply the curve's object-mode transforms to the position
|
||||
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root).
|
||||
*/
|
||||
if ((ik_data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
|
||||
mul_m4_v3(ik_data->tar->obmat, r_vec);
|
||||
}
|
||||
|
||||
/* Convert the position to pose-space. */
|
||||
mul_m4_v3(ob->imat, r_vec);
|
||||
|
||||
/* Set the new radius (it should be the average value). */
|
||||
*r_radius = (radius + *r_radius) / 2;
|
||||
}
|
||||
|
||||
/* This function positions the tail of the bone so that it preserves the length of it.
|
||||
* The length of the bone can be seen as a sphere radius.
|
||||
*/
|
||||
static int position_tail_on_spline(bSplineIKConstraint *ik_data,
|
||||
const float head_pos[3],
|
||||
const float sphere_radius,
|
||||
const int prev_seg_idx,
|
||||
float r_tail_pos[3],
|
||||
float *r_new_curve_pos,
|
||||
float *r_radius)
|
||||
{
|
||||
/* This is using the tessellated curve data.
|
||||
* So we are working with piece-wise linear curve segments.
|
||||
* The same method is use in #BKE_where_on_path to get curve location data. */
|
||||
const CurveCache *cache = ik_data->tar->runtime.curve_cache;
|
||||
const BevList *bl = cache->bev.first;
|
||||
BevPoint *bp = bl->bevpoints;
|
||||
const float spline_len = BKE_anim_path_get_length(cache);
|
||||
const float *seg_accum_len = cache->anim_path_accum_length;
|
||||
|
||||
int max_seg_idx = BKE_anim_path_get_array_size(cache) - 1;
|
||||
|
||||
/* Convert our initial intersection point guess to a point index.
|
||||
* If the curve was a straight line, then pointEnd would be the correct location.
|
||||
* So make it our first initial guess.
|
||||
*/
|
||||
const float guessed_len = *r_new_curve_pos * spline_len;
|
||||
|
||||
BLI_assert(prev_seg_idx >= 0);
|
||||
|
||||
int cur_seg_idx = prev_seg_idx;
|
||||
while (cur_seg_idx < max_seg_idx && guessed_len > seg_accum_len[cur_seg_idx]) {
|
||||
cur_seg_idx++;
|
||||
}
|
||||
|
||||
int bp_idx = cur_seg_idx + 1;
|
||||
|
||||
bp = bp + bp_idx;
|
||||
bool is_cyclic = bl->poly >= 0;
|
||||
BevPoint *prev_bp = bp - 1;
|
||||
|
||||
/* Go to the next tessellated curve point until we cross to outside of the sphere. */
|
||||
while (len_v3v3(head_pos, bp->vec) < sphere_radius) {
|
||||
if (bp_idx > max_seg_idx) {
|
||||
/* We are outside the defined curve. We will now extrapolate the intersection point. */
|
||||
break;
|
||||
}
|
||||
prev_bp = bp;
|
||||
if (is_cyclic && bp_idx == max_seg_idx) {
|
||||
/* Wrap around to the start point.
|
||||
* Don't set the bp_idx to zero here as we use it to get the segment index later.
|
||||
*/
|
||||
bp = bl->bevpoints;
|
||||
}
|
||||
else {
|
||||
bp++;
|
||||
}
|
||||
bp_idx++;
|
||||
}
|
||||
|
||||
float isect_1[3], isect_2[3];
|
||||
|
||||
/* Calculate the intersection point. */
|
||||
isect_line_sphere_v3(prev_bp->vec, bp->vec, head_pos, sphere_radius, isect_1, isect_2);
|
||||
|
||||
/* Because of how `isect_line_sphere_v3` works, we know that `isect_1` contains the
|
||||
* intersection point we want. And it will always intersect as we go from inside to outside
|
||||
* of the sphere.
|
||||
*/
|
||||
copy_v3_v3(r_tail_pos, isect_1);
|
||||
|
||||
cur_seg_idx = bp_idx - 2;
|
||||
float prev_seg_len = 0;
|
||||
|
||||
if (cur_seg_idx < 0) {
|
||||
cur_seg_idx = 0;
|
||||
prev_seg_len = 0;
|
||||
}
|
||||
else {
|
||||
prev_seg_len = seg_accum_len[cur_seg_idx];
|
||||
}
|
||||
|
||||
/* Convert the point back into the 0-1 interpolation range. */
|
||||
const float isect_seg_len = len_v3v3(prev_bp->vec, isect_1);
|
||||
const float frac = isect_seg_len / len_v3v3(prev_bp->vec, bp->vec);
|
||||
*r_new_curve_pos = (prev_seg_len + isect_seg_len) / spline_len;
|
||||
|
||||
if (*r_new_curve_pos > 1.0f) {
|
||||
*r_radius = bp->radius;
|
||||
}
|
||||
else {
|
||||
*r_radius = (1.0f - frac) * prev_bp->radius + frac * bp->radius;
|
||||
}
|
||||
|
||||
return cur_seg_idx;
|
||||
}
|
||||
|
||||
/* Evaluate spline IK for a given bone. */
|
||||
static void splineik_evaluate_bone(
|
||||
tSplineIK_Tree *tree, Object *ob, bPoseChannel *pchan, int index, tSplineIk_EvalState *state)
|
||||
{
|
||||
bSplineIKConstraint *ikData = tree->ikData;
|
||||
float origHead[3], origTail[3], poseHead[3], poseTail[3], basePoseMat[3][3], poseMat[3][3];
|
||||
float splineVec[3], scaleFac, radius = 1.0f;
|
||||
float tailBlendFac = 0.0f;
|
||||
bSplineIKConstraint *ik_data = tree->ik_data;
|
||||
|
||||
mul_v3_m4v3(poseHead, state->locrot_offset, pchan->pose_head);
|
||||
mul_v3_m4v3(poseTail, state->locrot_offset, pchan->pose_tail);
|
||||
if (pchan->bone->length == 0.0f) {
|
||||
/* Only move the bone position with zero length bones. */
|
||||
float bone_pos[4], dir[3], rad;
|
||||
BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, dir, NULL, &rad, NULL);
|
||||
|
||||
copy_v3_v3(origHead, poseHead);
|
||||
apply_curve_transform(ik_data, ob, rad, bone_pos, &rad);
|
||||
|
||||
/* first, adjust the point positions on the curve */
|
||||
float curveLen = tree->points[index] - tree->points[index + 1];
|
||||
float pointStart = state->curve_position;
|
||||
float poseScale = len_v3v3(poseHead, poseTail) / pchan->bone->length;
|
||||
float baseScale = 1.0f;
|
||||
|
||||
if (ikData->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
|
||||
/* Carry over the bone Y scale to the curve range. */
|
||||
baseScale = poseScale;
|
||||
copy_v3_v3(pchan->pose_mat[3], bone_pos);
|
||||
copy_v3_v3(pchan->pose_head, bone_pos);
|
||||
copy_v3_v3(pchan->pose_tail, bone_pos);
|
||||
pchan->flag |= POSE_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
float pointEnd = pointStart + curveLen * baseScale * state->curve_scale;
|
||||
float orig_head[3], orig_tail[3], pose_head[3], pose_tail[3];
|
||||
float base_pose_mat[3][3], pose_mat[3][3];
|
||||
float spline_vec[3], scale_fac, radius = 1.0f;
|
||||
float tail_blend_fac = 0.0f;
|
||||
|
||||
state->curve_position = pointEnd;
|
||||
mul_v3_m4v3(pose_head, state->locrot_offset, pchan->pose_head);
|
||||
mul_v3_m4v3(pose_tail, state->locrot_offset, pchan->pose_tail);
|
||||
|
||||
/* step 1: determine the positions for the endpoints of the bone */
|
||||
if (pointStart < 1.0f) {
|
||||
copy_v3_v3(orig_head, pose_head);
|
||||
|
||||
/* First, adjust the point positions on the curve. */
|
||||
float curveLen = tree->points[index] - tree->points[index + 1];
|
||||
float bone_len = len_v3v3(pose_head, pose_tail);
|
||||
float point_start = state->curve_position;
|
||||
float pose_scale = bone_len / pchan->bone->length;
|
||||
float base_scale = 1.0f;
|
||||
|
||||
if (ik_data->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
|
||||
/* Carry over the bone Y scale to the curve range. */
|
||||
base_scale = pose_scale;
|
||||
}
|
||||
|
||||
float point_end = point_start + curveLen * base_scale * state->curve_scale;
|
||||
|
||||
state->curve_position = point_end;
|
||||
|
||||
/* Step 1: determine the positions for the endpoints of the bone. */
|
||||
if (point_start < 1.0f) {
|
||||
float vec[4], dir[3], rad;
|
||||
radius = 0.0f;
|
||||
|
||||
/* determine if the bone should still be affected by SplineIK */
|
||||
if (pointEnd >= 1.0f) {
|
||||
/* blending factor depends on the amount of the bone still left on the chain */
|
||||
tailBlendFac = (1.0f - pointStart) / (pointEnd - pointStart);
|
||||
/* Calculate head position. */
|
||||
if (point_start == 0.0f) {
|
||||
/* Start of the path. We have no previous tail position to copy. */
|
||||
BKE_where_on_path(ik_data->tar, point_start, vec, dir, NULL, &rad, NULL);
|
||||
}
|
||||
else {
|
||||
tailBlendFac = 1.0f;
|
||||
copy_v3_v3(vec, state->prev_tail_loc);
|
||||
rad = state->prev_tail_radius;
|
||||
}
|
||||
|
||||
/* tail endpoint */
|
||||
if (BKE_where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
|
||||
/* apply curve's object-mode transforms to the position
|
||||
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
|
||||
*/
|
||||
if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
|
||||
mul_m4_v3(ikData->tar->obmat, vec);
|
||||
radius = rad;
|
||||
copy_v3_v3(pose_head, vec);
|
||||
apply_curve_transform(ik_data, ob, rad, pose_head, &radius);
|
||||
|
||||
/* Calculate tail position. */
|
||||
if (ik_data->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) {
|
||||
float sphere_radius;
|
||||
|
||||
if (ik_data->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
|
||||
sphere_radius = bone_len;
|
||||
}
|
||||
else {
|
||||
/* Don't take bone scale into account. */
|
||||
sphere_radius = pchan->bone->length;
|
||||
}
|
||||
|
||||
/* convert the position to pose-space, then store it */
|
||||
mul_m4_v3(ob->imat, vec);
|
||||
copy_v3_v3(poseTail, vec);
|
||||
/* Calculate the tail position with sphere curve intersection. */
|
||||
state->prev_tail_seg_idx = position_tail_on_spline(
|
||||
ik_data, vec, sphere_radius, state->prev_tail_seg_idx, pose_tail, &point_end, &rad);
|
||||
|
||||
/* set the new radius */
|
||||
radius = rad;
|
||||
state->prev_tail_radius = rad;
|
||||
copy_v3_v3(state->prev_tail_loc, pose_tail);
|
||||
|
||||
apply_curve_transform(ik_data, ob, rad, pose_tail, &radius);
|
||||
state->curve_position = point_end;
|
||||
}
|
||||
else {
|
||||
/* Scale to fit curve end position. */
|
||||
if (BKE_where_on_path(ik_data->tar, point_end, vec, dir, NULL, &rad, NULL)) {
|
||||
state->prev_tail_radius = rad;
|
||||
copy_v3_v3(state->prev_tail_loc, vec);
|
||||
copy_v3_v3(pose_tail, vec);
|
||||
apply_curve_transform(ik_data, ob, rad, pose_tail, &radius);
|
||||
}
|
||||
}
|
||||
|
||||
/* head endpoint */
|
||||
if (BKE_where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
|
||||
/* apply curve's object-mode transforms to the position
|
||||
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
|
||||
*/
|
||||
if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
|
||||
mul_m4_v3(ikData->tar->obmat, vec);
|
||||
}
|
||||
|
||||
/* store the position, and convert it to pose space */
|
||||
mul_m4_v3(ob->imat, vec);
|
||||
copy_v3_v3(poseHead, vec);
|
||||
|
||||
/* set the new radius (it should be the average value) */
|
||||
radius = (radius + rad) / 2;
|
||||
/* Determine if the bone should still be affected by SplineIK.
|
||||
* This makes it so that the bone slowly becomes poseable again the further it rolls off the
|
||||
* curve. When the whole bone has rolled off the curve, the IK constraint will not influence it
|
||||
* anymore.
|
||||
*/
|
||||
if (point_end >= 1.0f) {
|
||||
/* Blending factor depends on the amount of the bone still left on the chain. */
|
||||
tail_blend_fac = (1.0f - point_start) / (point_end - point_start);
|
||||
}
|
||||
else {
|
||||
tail_blend_fac = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,11 +489,8 @@ static void splineik_evaluate_bone(
|
|||
* - splineVec: the vector direction that the spline applies on the bone.
|
||||
* - scaleFac: the factor that the bone length is scaled by to get the desired amount.
|
||||
*/
|
||||
sub_v3_v3v3(splineVec, poseTail, poseHead);
|
||||
scaleFac = len_v3(splineVec) / pchan->bone->length;
|
||||
|
||||
/* Extrapolate the full length of the bone as it rolls off the end of the curve. */
|
||||
scaleFac = (tailBlendFac < 1e-5f) ? baseScale : scaleFac / tailBlendFac;
|
||||
sub_v3_v3v3(spline_vec, pose_tail, pose_head);
|
||||
scale_fac = len_v3(spline_vec) / pchan->bone->length;
|
||||
|
||||
/* Step 3: compute the shortest rotation needed
|
||||
* to map from the bone rotation to the current axis.
|
||||
|
@ -350,83 +501,83 @@ static void splineik_evaluate_bone(
|
|||
float dmat[3][3], rmat[3][3];
|
||||
float raxis[3], rangle;
|
||||
|
||||
/* compute the raw rotation matrix from the bone's current matrix by extracting only the
|
||||
* orientation-relevant axes, and normalizing them
|
||||
/* Compute the raw rotation matrix from the bone's current matrix by extracting only the
|
||||
* orientation-relevant axes, and normalizing them.
|
||||
*/
|
||||
mul_m3_m4m4(basePoseMat, state->locrot_offset, pchan->pose_mat);
|
||||
normalize_m3_m3(rmat, basePoseMat);
|
||||
mul_m3_m4m4(base_pose_mat, state->locrot_offset, pchan->pose_mat);
|
||||
normalize_m3_m3(rmat, base_pose_mat);
|
||||
|
||||
/* Also, normalize the orientation imposed by the bone,
|
||||
* now that we've extracted the scale factor. */
|
||||
normalize_v3(splineVec);
|
||||
normalize_v3(spline_vec);
|
||||
|
||||
/* calculate smallest axis-angle rotation necessary for getting from the
|
||||
* current orientation of the bone, to the spline-imposed direction
|
||||
/* Calculate smallest axis-angle rotation necessary for getting from the
|
||||
* current orientation of the bone, to the spline-imposed direction.
|
||||
*/
|
||||
cross_v3_v3v3(raxis, rmat[1], splineVec);
|
||||
cross_v3_v3v3(raxis, rmat[1], spline_vec);
|
||||
|
||||
rangle = dot_v3v3(rmat[1], splineVec);
|
||||
rangle = dot_v3v3(rmat[1], spline_vec);
|
||||
CLAMP(rangle, -1.0f, 1.0f);
|
||||
rangle = acosf(rangle);
|
||||
|
||||
/* multiply the magnitude of the angle by the influence of the constraint to
|
||||
* control the influence of the SplineIK effect
|
||||
/* Multiply the magnitude of the angle by the influence of the constraint to
|
||||
* control the influence of the SplineIK effect.
|
||||
*/
|
||||
rangle *= tree->con->enforce * tailBlendFac;
|
||||
rangle *= tree->con->enforce * tail_blend_fac;
|
||||
|
||||
/* construct rotation matrix from the axis-angle rotation found above
|
||||
* - this call takes care to make sure that the axis provided is a unit vector first
|
||||
/* Construct rotation matrix from the axis-angle rotation found above.
|
||||
* - This call takes care to make sure that the axis provided is a unit vector first.
|
||||
*/
|
||||
axis_angle_to_mat3(dmat, raxis, rangle);
|
||||
|
||||
/* Combine these rotations so that the y-axis of the bone is now aligned as the
|
||||
* spline dictates, while still maintaining roll control from the existing bone animation. */
|
||||
mul_m3_m3m3(poseMat, dmat, rmat);
|
||||
mul_m3_m3m3(pose_mat, dmat, rmat);
|
||||
|
||||
/* attempt to reduce shearing, though I doubt this'll really help too much now... */
|
||||
normalize_m3(poseMat);
|
||||
/* Attempt to reduce shearing, though I doubt this'll really help too much now... */
|
||||
normalize_m3(pose_mat);
|
||||
|
||||
mul_m3_m3m3(basePoseMat, dmat, basePoseMat);
|
||||
mul_m3_m3m3(base_pose_mat, dmat, base_pose_mat);
|
||||
|
||||
/* apply rotation to the accumulated parent transform */
|
||||
/* Apply rotation to the accumulated parent transform. */
|
||||
mul_m4_m3m4(state->locrot_offset, dmat, state->locrot_offset);
|
||||
}
|
||||
|
||||
/* step 4: set the scaling factors for the axes */
|
||||
/* Step 4: Set the scaling factors for the axes. */
|
||||
|
||||
/* Always multiply the y-axis by the scaling factor to get the correct length. */
|
||||
mul_v3_fl(poseMat[1], scaleFac);
|
||||
mul_v3_fl(pose_mat[1], scale_fac);
|
||||
|
||||
/* After that, apply x/z scaling modes. */
|
||||
if (ikData->xzScaleMode != CONSTRAINT_SPLINEIK_XZS_NONE) {
|
||||
if (ik_data->xzScaleMode != CONSTRAINT_SPLINEIK_XZS_NONE) {
|
||||
/* First, apply the original scale if enabled. */
|
||||
if (ikData->xzScaleMode == CONSTRAINT_SPLINEIK_XZS_ORIGINAL ||
|
||||
(ikData->flag & CONSTRAINT_SPLINEIK_USE_ORIGINAL_SCALE) != 0) {
|
||||
if (ik_data->xzScaleMode == CONSTRAINT_SPLINEIK_XZS_ORIGINAL ||
|
||||
(ik_data->flag & CONSTRAINT_SPLINEIK_USE_ORIGINAL_SCALE) != 0) {
|
||||
float scale;
|
||||
|
||||
/* x-axis scale */
|
||||
/* X-axis scale. */
|
||||
scale = len_v3(pchan->pose_mat[0]);
|
||||
mul_v3_fl(poseMat[0], scale);
|
||||
/* z-axis scale */
|
||||
mul_v3_fl(pose_mat[0], scale);
|
||||
/* Z-axis scale. */
|
||||
scale = len_v3(pchan->pose_mat[2]);
|
||||
mul_v3_fl(poseMat[2], scale);
|
||||
mul_v3_fl(pose_mat[2], scale);
|
||||
|
||||
/* Adjust the scale factor used for volume preservation
|
||||
* to consider the pre-IK scaling as the initial volume. */
|
||||
scaleFac /= poseScale;
|
||||
scale_fac /= pose_scale;
|
||||
}
|
||||
|
||||
/* Apply volume preservation. */
|
||||
switch (ikData->xzScaleMode) {
|
||||
switch (ik_data->xzScaleMode) {
|
||||
case CONSTRAINT_SPLINEIK_XZS_INVERSE: {
|
||||
/* old 'volume preservation' method using the inverse scale */
|
||||
/* Old 'volume preservation' method using the inverse scale. */
|
||||
float scale;
|
||||
|
||||
/* calculate volume preservation factor which is
|
||||
* basically the inverse of the y-scaling factor
|
||||
/* Calculate volume preservation factor which is
|
||||
* basically the inverse of the y-scaling factor.
|
||||
*/
|
||||
if (fabsf(scaleFac) != 0.0f) {
|
||||
scale = 1.0f / fabsf(scaleFac);
|
||||
if (fabsf(scale_fac) != 0.0f) {
|
||||
scale = 1.0f / fabsf(scale_fac);
|
||||
|
||||
/* We need to clamp this within sensible values. */
|
||||
/* NOTE: these should be fine for now, but should get sanitized in future. */
|
||||
|
@ -436,56 +587,56 @@ static void splineik_evaluate_bone(
|
|||
scale = 1.0f;
|
||||
}
|
||||
|
||||
/* apply the scaling */
|
||||
mul_v3_fl(poseMat[0], scale);
|
||||
mul_v3_fl(poseMat[2], scale);
|
||||
/* Apply the scaling. */
|
||||
mul_v3_fl(pose_mat[0], scale);
|
||||
mul_v3_fl(pose_mat[2], scale);
|
||||
break;
|
||||
}
|
||||
case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC: {
|
||||
/* improved volume preservation based on the Stretch To constraint */
|
||||
/* Improved volume preservation based on the Stretch To constraint. */
|
||||
float final_scale;
|
||||
|
||||
/* as the basis for volume preservation, we use the inverse scale factor... */
|
||||
if (fabsf(scaleFac) != 0.0f) {
|
||||
/* NOTE: The method here is taken wholesale from the Stretch To constraint */
|
||||
float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge);
|
||||
/* As the basis for volume preservation, we use the inverse scale factor... */
|
||||
if (fabsf(scale_fac) != 0.0f) {
|
||||
/* NOTE: The method here is taken wholesale from the Stretch To constraint. */
|
||||
float bulge = powf(1.0f / fabsf(scale_fac), ik_data->bulge);
|
||||
|
||||
if (bulge > 1.0f) {
|
||||
if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
|
||||
float bulge_max = max_ff(ikData->bulge_max, 1.0f);
|
||||
if (ik_data->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
|
||||
float bulge_max = max_ff(ik_data->bulge_max, 1.0f);
|
||||
float hard = min_ff(bulge, bulge_max);
|
||||
|
||||
float range = bulge_max - 1.0f;
|
||||
float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
|
||||
float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2;
|
||||
|
||||
bulge = interpf(soft, hard, ikData->bulge_smooth);
|
||||
bulge = interpf(soft, hard, ik_data->bulge_smooth);
|
||||
}
|
||||
}
|
||||
if (bulge < 1.0f) {
|
||||
if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
|
||||
float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f);
|
||||
if (ik_data->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
|
||||
float bulge_min = CLAMPIS(ik_data->bulge_min, 0.0f, 1.0f);
|
||||
float hard = max_ff(bulge, bulge_min);
|
||||
|
||||
float range = 1.0f - bulge_min;
|
||||
float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
|
||||
float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2;
|
||||
|
||||
bulge = interpf(soft, hard, ikData->bulge_smooth);
|
||||
bulge = interpf(soft, hard, ik_data->bulge_smooth);
|
||||
}
|
||||
}
|
||||
|
||||
/* compute scale factor for xz axes from this value */
|
||||
/* Compute scale factor for xz axes from this value. */
|
||||
final_scale = sqrtf(bulge);
|
||||
}
|
||||
else {
|
||||
/* no scaling, so scale factor is simple */
|
||||
/* No scaling, so scale factor is simple. */
|
||||
final_scale = 1.0f;
|
||||
}
|
||||
|
||||
/* Apply the scaling (assuming normalized scale). */
|
||||
mul_v3_fl(poseMat[0], final_scale);
|
||||
mul_v3_fl(poseMat[2], final_scale);
|
||||
mul_v3_fl(pose_mat[0], final_scale);
|
||||
mul_v3_fl(pose_mat[2], final_scale);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -494,49 +645,49 @@ static void splineik_evaluate_bone(
|
|||
/* Finally, multiply the x and z scaling by the radius of the curve too,
|
||||
* to allow automatic scales to get tweaked still.
|
||||
*/
|
||||
if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
|
||||
mul_v3_fl(poseMat[0], radius);
|
||||
mul_v3_fl(poseMat[2], radius);
|
||||
if ((ik_data->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
|
||||
mul_v3_fl(pose_mat[0], radius);
|
||||
mul_v3_fl(pose_mat[2], radius);
|
||||
}
|
||||
|
||||
/* Blend the scaling of the matrix according to the influence. */
|
||||
sub_m3_m3m3(poseMat, poseMat, basePoseMat);
|
||||
madd_m3_m3m3fl(poseMat, basePoseMat, poseMat, tree->con->enforce * tailBlendFac);
|
||||
sub_m3_m3m3(pose_mat, pose_mat, base_pose_mat);
|
||||
madd_m3_m3m3fl(pose_mat, base_pose_mat, pose_mat, tree->con->enforce * tail_blend_fac);
|
||||
|
||||
/* step 5: set the location of the bone in the matrix */
|
||||
if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
|
||||
/* when the 'no-root' option is affected, the chain can retain
|
||||
* the shape but be moved elsewhere
|
||||
/* Step 5: Set the location of the bone in the matrix. */
|
||||
if (ik_data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
|
||||
/* When the 'no-root' option is affected, the chain can retain
|
||||
* the shape but be moved elsewhere.
|
||||
*/
|
||||
copy_v3_v3(poseHead, origHead);
|
||||
copy_v3_v3(pose_head, orig_head);
|
||||
}
|
||||
else if (tree->con->enforce < 1.0f) {
|
||||
/* when the influence is too low
|
||||
* - blend the positions for the 'root' bone
|
||||
* - stick to the parent for any other
|
||||
/* When the influence is too low:
|
||||
* - Blend the positions for the 'root' bone.
|
||||
* - Stick to the parent for any other.
|
||||
*/
|
||||
if (index < tree->chainlen - 1) {
|
||||
copy_v3_v3(poseHead, origHead);
|
||||
copy_v3_v3(pose_head, orig_head);
|
||||
}
|
||||
else {
|
||||
interp_v3_v3v3(poseHead, origHead, poseHead, tree->con->enforce);
|
||||
interp_v3_v3v3(pose_head, orig_head, pose_head, tree->con->enforce);
|
||||
}
|
||||
}
|
||||
|
||||
/* finally, store the new transform */
|
||||
copy_m4_m3(pchan->pose_mat, poseMat);
|
||||
copy_v3_v3(pchan->pose_mat[3], poseHead);
|
||||
copy_v3_v3(pchan->pose_head, poseHead);
|
||||
/* Finally, store the new transform. */
|
||||
copy_m4_m3(pchan->pose_mat, pose_mat);
|
||||
copy_v3_v3(pchan->pose_mat[3], pose_head);
|
||||
copy_v3_v3(pchan->pose_head, pose_head);
|
||||
|
||||
mul_v3_mat3_m4v3(origTail, state->locrot_offset, pchan->pose_tail);
|
||||
mul_v3_mat3_m4v3(orig_tail, state->locrot_offset, pchan->pose_tail);
|
||||
|
||||
/* recalculate tail, as it's now outdated after the head gets adjusted above! */
|
||||
/* Recalculate tail, as it's now outdated after the head gets adjusted above! */
|
||||
BKE_pose_where_is_bone_tail(pchan);
|
||||
|
||||
/* update the offset in the accumulated parent transform */
|
||||
sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, origTail);
|
||||
/* Update the offset in the accumulated parent transform. */
|
||||
sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, orig_tail);
|
||||
|
||||
/* done! */
|
||||
/* Done! */
|
||||
pchan->flag |= POSE_DONE;
|
||||
}
|
||||
|
||||
|
@ -559,8 +710,8 @@ static void splineik_execute_tree(
|
|||
|
||||
if (splineik_evaluate_init(tree, &state)) {
|
||||
/* Walk over each bone in the chain, calculating the effects of spline IK
|
||||
* - the chain is traversed in the opposite order to storage order (i.e. parent to children)
|
||||
* so that dependencies are correct
|
||||
* - the chain is traversed in the opposite order to storage order
|
||||
* (i.e. parent to children) so that dependencies are correct
|
||||
*/
|
||||
for (int i = tree->chainlen - 1; i >= 0; i--) {
|
||||
bPoseChannel *pchan = tree->chain[i];
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "NOD_node_tree_multi_function.hh"
|
||||
#include "NOD_type_conversions.hh"
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
|
||||
|
@ -210,23 +210,25 @@ class ConvertedReadAttribute final : public ReadAttribute {
|
|||
const CPPType &from_type_;
|
||||
const CPPType &to_type_;
|
||||
ReadAttributePtr base_attribute_;
|
||||
const nodes::DataTypeConversions &conversions_;
|
||||
void (*convert_)(const void *src, void *dst);
|
||||
|
||||
public:
|
||||
ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
|
||||
: ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
|
||||
from_type_(base_attribute->cpp_type()),
|
||||
to_type_(to_type),
|
||||
base_attribute_(std::move(base_attribute)),
|
||||
conversions_(nodes::get_implicit_type_conversions())
|
||||
base_attribute_(std::move(base_attribute))
|
||||
{
|
||||
const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
|
||||
convert_ = conversions.get_conversion_functions(base_attribute_->cpp_type(), to_type)
|
||||
->convert_single_to_uninitialized;
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
|
||||
base_attribute_->get(index, buffer);
|
||||
conversions_.convert(from_type_, to_type_, buffer, r_value);
|
||||
convert_(buffer, r_value);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -989,7 +991,7 @@ blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_rea
|
|||
BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type));
|
||||
|
||||
void *out_value = alloca(out_cpp_type->size());
|
||||
conversions.convert(*in_cpp_type, *out_cpp_type, value, out_value);
|
||||
conversions.convert_to_uninitialized(*in_cpp_type, *out_cpp_type, value, out_value);
|
||||
|
||||
const int domain_size = this->attribute_domain_size(domain);
|
||||
blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>(
|
||||
|
|
|
@ -132,7 +132,7 @@ static void blender_version_init(void)
|
|||
|
||||
BLI_snprintf(blender_version_string,
|
||||
ARRAY_SIZE(blender_version_string),
|
||||
"%d.%02d.%d%s",
|
||||
"%d.%01d.%d%s",
|
||||
BLENDER_VERSION / 100,
|
||||
BLENDER_VERSION % 100,
|
||||
BLENDER_VERSION_PATCH,
|
||||
|
|
|
@ -414,6 +414,7 @@ void BKE_curve_init(Curve *cu, const short curve_type)
|
|||
cu->tb[0].w = cu->tb[0].h = 0.0;
|
||||
}
|
||||
else if (cu->type == OB_SURF) {
|
||||
cu->flag |= CU_3D;
|
||||
cu->resolv = 4;
|
||||
}
|
||||
cu->bevel_profile = NULL;
|
||||
|
|
|
@ -814,14 +814,16 @@ static void set_loop_uv(MLoopUV &uv, const float2 &co)
|
|||
|
||||
static Color4f get_loop_color(const MLoopCol &col)
|
||||
{
|
||||
Color4f value;
|
||||
rgba_uchar_to_float(value, &col.r);
|
||||
return value;
|
||||
Color4f srgb_color;
|
||||
rgba_uchar_to_float(srgb_color, &col.r);
|
||||
Color4f linear_color;
|
||||
srgb_to_linearrgb_v4(linear_color, srgb_color);
|
||||
return linear_color;
|
||||
}
|
||||
|
||||
static void set_loop_color(MLoopCol &col, const Color4f &value)
|
||||
static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
|
||||
{
|
||||
rgba_float_to_uchar(&col.r, value);
|
||||
linearrgb_to_srgb_uchar4(&col.r, linear_color);
|
||||
}
|
||||
|
||||
static float get_crease(const MEdge &edge)
|
||||
|
|
|
@ -2007,7 +2007,8 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type)
|
|||
/* do an extra poll here, because some int types are used
|
||||
* for multiple node types, this helps find the desired type
|
||||
*/
|
||||
if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree))) {
|
||||
const char *disabled_hint;
|
||||
if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint))) {
|
||||
idname = ntype->idname;
|
||||
break;
|
||||
}
|
||||
|
@ -4407,15 +4408,17 @@ static void node_type_base_defaults(bNodeType *ntype)
|
|||
}
|
||||
|
||||
/* allow this node for any tree type */
|
||||
static bool node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *UNUSED(ntree))
|
||||
static bool node_poll_default(bNodeType *UNUSED(ntype),
|
||||
bNodeTree *UNUSED(ntree),
|
||||
const char **UNUSED(disabled_hint))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* use the basic poll function */
|
||||
static bool node_poll_instance_default(bNode *node, bNodeTree *ntree)
|
||||
static bool node_poll_instance_default(bNode *node, bNodeTree *ntree, const char **disabled_hint)
|
||||
{
|
||||
return node->typeinfo->poll(node->typeinfo, ntree);
|
||||
return node->typeinfo->poll(node->typeinfo, ntree, disabled_hint);
|
||||
}
|
||||
|
||||
/* NOLINTNEXTLINE: readability-function-size */
|
||||
|
@ -4634,7 +4637,9 @@ void node_type_internal_links(bNodeType *ntype,
|
|||
|
||||
/* callbacks for undefined types */
|
||||
|
||||
static bool node_undefined_poll(bNodeType *UNUSED(ntype), bNodeTree *UNUSED(nodetree))
|
||||
static bool node_undefined_poll(bNodeType *UNUSED(ntype),
|
||||
bNodeTree *UNUSED(nodetree),
|
||||
const char **UNUSED(r_disabled_hint))
|
||||
{
|
||||
/* this type can not be added deliberately, it's just a placeholder */
|
||||
return false;
|
||||
|
|
|
@ -163,6 +163,6 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
|
|||
const CustomDataType data_type)
|
||||
{
|
||||
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
|
||||
node_ui_storage.attribute_hints.add_as(attribute_name,
|
||||
AvailableAttributeInfo{domain, data_type});
|
||||
node_ui_storage.attribute_hints.add_as(
|
||||
AvailableAttributeInfo{attribute_name, domain, data_type});
|
||||
}
|
||||
|
|
|
@ -1761,9 +1761,9 @@ void BKE_object_free_derived_caches(Object *ob)
|
|||
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
|
||||
ob->runtime.geometry_set_eval = NULL;
|
||||
}
|
||||
if (ob->runtime.geometry_set_preview != NULL) {
|
||||
BKE_geometry_set_free(ob->runtime.geometry_set_preview);
|
||||
ob->runtime.geometry_set_preview = NULL;
|
||||
if (ob->runtime.geometry_set_previews != NULL) {
|
||||
BLI_ghash_free(ob->runtime.geometry_set_previews, NULL, (GHashValFreeFP)BKE_geometry_set_free);
|
||||
ob->runtime.geometry_set_previews = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1816,14 +1816,20 @@ void BKE_object_free_caches(Object *object)
|
|||
}
|
||||
|
||||
/* Can be called from multiple threads. */
|
||||
void BKE_object_set_preview_geometry_set(Object *ob, struct GeometrySet *geometry_set)
|
||||
void BKE_object_preview_geometry_set_add(Object *ob,
|
||||
const uint64_t key,
|
||||
struct GeometrySet *geometry_set)
|
||||
{
|
||||
static ThreadMutex mutex = BLI_MUTEX_INITIALIZER;
|
||||
BLI_mutex_lock(&mutex);
|
||||
if (ob->runtime.geometry_set_preview != NULL) {
|
||||
BKE_geometry_set_free(ob->runtime.geometry_set_preview);
|
||||
if (ob->runtime.geometry_set_previews == NULL) {
|
||||
ob->runtime.geometry_set_previews = BLI_ghash_int_new(__func__);
|
||||
}
|
||||
ob->runtime.geometry_set_preview = geometry_set;
|
||||
BLI_ghash_reinsert(ob->runtime.geometry_set_previews,
|
||||
POINTER_FROM_UINT(key),
|
||||
geometry_set,
|
||||
NULL,
|
||||
(GHashValFreeFP)BKE_geometry_set_free);
|
||||
BLI_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rand.h"
|
||||
|
@ -68,6 +69,8 @@
|
|||
#include "BLI_hash.h"
|
||||
#include "BLI_strict_flags.h"
|
||||
|
||||
using blender::Array;
|
||||
using blender::float3;
|
||||
using blender::float4x4;
|
||||
using blender::Span;
|
||||
|
||||
|
@ -912,40 +915,37 @@ struct FaceDupliData_EditMesh {
|
|||
const float (*vert_coords)[3];
|
||||
};
|
||||
|
||||
static void get_dupliface_transform_from_coords(const float coords[][3],
|
||||
const int coords_len,
|
||||
static void get_dupliface_transform_from_coords(Span<float3> coords,
|
||||
const bool use_scale,
|
||||
const float scale_fac,
|
||||
float r_mat[4][4])
|
||||
{
|
||||
float loc[3], quat[4], scale, size[3];
|
||||
|
||||
/* Location. */
|
||||
{
|
||||
const float w = 1.0f / (float)coords_len;
|
||||
zero_v3(loc);
|
||||
for (int i = 0; i < coords_len; i++) {
|
||||
madd_v3_v3fl(loc, coords[i], w);
|
||||
}
|
||||
float3 location(0);
|
||||
for (const float3 &coord : coords) {
|
||||
location += coord;
|
||||
}
|
||||
location *= 1.0f / (float)coords.size();
|
||||
|
||||
/* Rotation. */
|
||||
{
|
||||
float f_no[3];
|
||||
cross_poly_v3(f_no, coords, (uint)coords_len);
|
||||
normalize_v3(f_no);
|
||||
tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
|
||||
}
|
||||
float quat[4];
|
||||
|
||||
float3 f_no;
|
||||
cross_poly_v3(f_no, (const float(*)[3])coords.data(), (uint)coords.size());
|
||||
f_no.normalize();
|
||||
tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
|
||||
|
||||
/* Scale. */
|
||||
float scale;
|
||||
if (use_scale) {
|
||||
const float area = area_poly_v3(coords, (uint)coords_len);
|
||||
const float area = area_poly_v3((const float(*)[3])coords.data(), (uint)coords.size());
|
||||
scale = sqrtf(area) * scale_fac;
|
||||
}
|
||||
else {
|
||||
scale = 1.0f;
|
||||
}
|
||||
size[0] = size[1] = size[2] = scale;
|
||||
|
||||
loc_quat_size_to_mat4(r_mat, loc, quat, size);
|
||||
loc_quat_size_to_mat4(r_mat, location, quat, float3(scale));
|
||||
}
|
||||
|
||||
static DupliObject *face_dupli(const DupliContext *ctx,
|
||||
|
@ -954,14 +954,13 @@ static DupliObject *face_dupli(const DupliContext *ctx,
|
|||
const int index,
|
||||
const bool use_scale,
|
||||
const float scale_fac,
|
||||
const float (*coords)[3],
|
||||
const int coords_len)
|
||||
Span<float3> coords)
|
||||
{
|
||||
float obmat[4][4];
|
||||
float space_mat[4][4];
|
||||
|
||||
/* `obmat` is transform to face. */
|
||||
get_dupliface_transform_from_coords(coords, coords_len, use_scale, scale_fac, obmat);
|
||||
get_dupliface_transform_from_coords(coords, use_scale, scale_fac, obmat);
|
||||
|
||||
/* Make offset relative to inst_ob using relative child transform. */
|
||||
mul_mat3_m4_v3(child_imat, obmat[3]);
|
||||
|
@ -989,7 +988,6 @@ static DupliObject *face_dupli(const DupliContext *ctx,
|
|||
return dob;
|
||||
}
|
||||
|
||||
/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
|
||||
static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
|
||||
Object *inst_ob,
|
||||
const float child_imat[4][4],
|
||||
|
@ -1003,17 +1001,16 @@ static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
|
|||
const MVert *mvert)
|
||||
{
|
||||
const int coords_len = mpoly->totloop;
|
||||
float(*coords)[3] = (float(*)[3])BLI_array_alloca(coords, (size_t)coords_len);
|
||||
Array<float3, 64> coords(coords_len);
|
||||
|
||||
const MLoop *ml = mloopstart;
|
||||
for (int i = 0; i < coords_len; i++, ml++) {
|
||||
copy_v3_v3(coords[i], mvert[ml->v].co);
|
||||
coords[i] = float3(mvert[ml->v].co);
|
||||
}
|
||||
|
||||
return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
|
||||
return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords);
|
||||
}
|
||||
|
||||
/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
|
||||
static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
|
||||
Object *inst_ob,
|
||||
const float child_imat[4][4],
|
||||
|
@ -1026,7 +1023,7 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
|
|||
const float (*vert_coords)[3])
|
||||
{
|
||||
const int coords_len = f->len;
|
||||
float(*coords)[3] = (float(*)[3])BLI_array_alloca(coords, (size_t)coords_len);
|
||||
Array<float3, 64> coords(coords_len);
|
||||
|
||||
BMLoop *l_first, *l_iter;
|
||||
int i = 0;
|
||||
|
@ -1042,7 +1039,7 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
|
|||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
|
||||
return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
|
||||
return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords);
|
||||
}
|
||||
|
||||
static void make_child_duplis_faces_from_mesh(const DupliContext *ctx,
|
||||
|
|
|
@ -227,7 +227,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
case SPACE_SPREADSHEET: {
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_ID(data, sspreadsheet->pinned_id, IDWALK_CB_NOP);
|
||||
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
|
||||
if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
|
||||
BKE_LIB_FOREACHID_PROCESS(
|
||||
data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1357,6 +1362,27 @@ static void write_area(BlendWriter *writer, ScrArea *area)
|
|||
BLO_write_struct(writer, SpreadsheetColumnID, column->id);
|
||||
BLO_write_string(writer, column->id->name);
|
||||
}
|
||||
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
|
||||
switch (context->type) {
|
||||
case SPREADSHEET_CONTEXT_OBJECT: {
|
||||
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
|
||||
BLO_write_struct(writer, SpreadsheetContextObject, object_context);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_CONTEXT_MODIFIER: {
|
||||
SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
|
||||
BLO_write_struct(writer, SpreadsheetContextModifier, modifier_context);
|
||||
BLO_write_string(writer, modifier_context->modifier_name);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_CONTEXT_NODE: {
|
||||
SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
|
||||
BLO_write_struct(writer, SpreadsheetContextNode, node_context);
|
||||
BLO_write_string(writer, node_context->node_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1715,6 +1741,25 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
|
|||
BLO_read_data_address(reader, &column->id);
|
||||
BLO_read_data_address(reader, &column->id->name);
|
||||
}
|
||||
|
||||
BLO_read_list(reader, &sspreadsheet->context_path);
|
||||
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
|
||||
switch (context->type) {
|
||||
case SPREADSHEET_CONTEXT_NODE: {
|
||||
SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
|
||||
BLO_read_data_address(reader, &node_context->node_name);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_CONTEXT_MODIFIER: {
|
||||
SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
|
||||
BLO_read_data_address(reader, &modifier_context->modifier_name);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_CONTEXT_OBJECT: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1931,7 +1976,12 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr
|
|||
}
|
||||
case SPACE_SPREADSHEET: {
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
|
||||
BLO_read_id_address(reader, parent_id->lib, &sspreadsheet->pinned_id);
|
||||
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
|
||||
if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
|
||||
BLO_read_id_address(
|
||||
reader, parent_id->lib, &((SpreadsheetContextObject *)context)->object);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -73,6 +73,12 @@ template<typename Key, typename Value> class MultiValueMap {
|
|||
vector.append(std::forward<ForwardValue>(value));
|
||||
}
|
||||
|
||||
void add_non_duplicates(const Key &key, const Value &value)
|
||||
{
|
||||
Vector<Value> &vector = map_.lookup_or_add_default_as(key);
|
||||
vector.append_non_duplicates(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all given values to the key.
|
||||
*/
|
||||
|
|
|
@ -3010,8 +3010,13 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
|
|||
else if (sl->spacetype == SPACE_SPREADSHEET) {
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
|
||||
|
||||
sspreadsheet->pinned_id = restore_pointer_by_name(
|
||||
id_map, sspreadsheet->pinned_id, USER_IGNORE);
|
||||
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
|
||||
if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
|
||||
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
|
||||
object_context->object = restore_pointer_by_name(
|
||||
id_map, (ID *)object_context->object, USER_IGNORE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2010,30 +2010,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
* \note Be sure to check when bumping the version:
|
||||
* - "versioning_userdef.c", #blo_do_versions_userdef
|
||||
* - "versioning_userdef.c", #do_versions_theme
|
||||
*
|
||||
* \note Keep this message at the bottom of the function.
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
LISTBASE_FOREACH (Brush *, br, &bmain->brushes) {
|
||||
BKE_brush_default_input_curves_set(br);
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "float", "smooth_strength_factor")) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
Sculpt *sd = scene->toolsettings->sculpt;
|
||||
if (sd) {
|
||||
sd->smooth_strength_factor = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) {
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density");
|
||||
|
@ -2067,5 +2044,44 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Consolidate node and final evaluation modes. */
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
|
||||
if (sl->spacetype == SPACE_SPREADSHEET) {
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
|
||||
if (sspreadsheet->object_eval_state == 2) {
|
||||
sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
* \note Be sure to check when bumping the version:
|
||||
* - "versioning_userdef.c", #blo_do_versions_userdef
|
||||
* - "versioning_userdef.c", #do_versions_theme
|
||||
*
|
||||
* \note Keep this message at the bottom of the function.
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
LISTBASE_FOREACH (Brush *, br, &bmain->brushes) {
|
||||
BKE_brush_default_input_curves_set(br);
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "float", "smooth_strength_factor")) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
Sculpt *sd = scene->toolsettings->sculpt;
|
||||
if (sd) {
|
||||
sd->smooth_strength_factor = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1805,7 +1805,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
|
|||
BMVert *v_kill,
|
||||
const bool do_del,
|
||||
const bool check_edge_exists,
|
||||
const bool kill_degenerate_faces)
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces)
|
||||
{
|
||||
BMEdge *e_old;
|
||||
BMVert *v_old, *v_target;
|
||||
|
@ -1840,6 +1841,9 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
|
|||
BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
|
||||
BMLoop *l_kill_next;
|
||||
|
||||
/* Candidates for being duplicate. */
|
||||
BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* For verification later, count valence of 'v_old' and 'v_target' */
|
||||
valence1 = bmesh_disk_count(v_old);
|
||||
|
@ -1877,9 +1881,14 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
|
|||
|
||||
/* fix len attribute of face */
|
||||
l_kill->f->len--;
|
||||
if (kill_degenerate_faces) {
|
||||
if (l_kill->f->len < 3) {
|
||||
BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
|
||||
if (kill_degenerate_faces && (l_kill->f->len < 3)) {
|
||||
BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
|
||||
}
|
||||
else {
|
||||
/* The duplicate test isn't reliable at this point as `e_splice` might be set,
|
||||
* so the duplicate test needs to run once the edge has been spliced. */
|
||||
if (kill_duplicate_faces) {
|
||||
BLI_SMALLSTACK_PUSH(faces_duplicate_candidate, l_kill->f);
|
||||
}
|
||||
}
|
||||
l_kill_next = l_kill->radial_next;
|
||||
|
@ -1940,6 +1949,15 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
|
|||
}
|
||||
}
|
||||
|
||||
if (kill_duplicate_faces) {
|
||||
BMFace *f_kill;
|
||||
while ((f_kill = BLI_SMALLSTACK_POP(faces_duplicate_candidate))) {
|
||||
if (BM_face_find_double(f_kill)) {
|
||||
BM_face_kill(bm, f_kill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BM_CHECK_ELEMENT(v_old);
|
||||
BM_CHECK_ELEMENT(v_target);
|
||||
BM_CHECK_ELEMENT(e_old);
|
||||
|
|
|
@ -115,7 +115,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
|
|||
BMVert *v_kill,
|
||||
const bool do_del,
|
||||
const bool check_edge_exists,
|
||||
const bool kill_degenerate_faces);
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces);
|
||||
BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
|
|
|
@ -72,7 +72,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
|
|||
}
|
||||
if (!v->e->l) {
|
||||
if (len == 2) {
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
|
||||
}
|
||||
/* used to kill the vertex here, but it may be connected to faces.
|
||||
* so better do nothing */
|
||||
|
@ -82,7 +82,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
|
|||
}
|
||||
if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
|
||||
/* boundary vertex on a face */
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
|
||||
}
|
||||
return BM_disk_dissolve(bm, v);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
|
|||
if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true))) {
|
||||
return false;
|
||||
}
|
||||
if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true))) {
|
||||
if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true, true))) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
@ -141,7 +141,7 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
|
|||
}
|
||||
if (keepedge == NULL && len == 2) {
|
||||
/* collapse the vertex */
|
||||
e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true);
|
||||
e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true, true);
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
|
@ -184,7 +184,8 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
|
|||
|
||||
/* collapse the vertex */
|
||||
/* note, the baseedge can be a boundary of manifold, use this as join_faces arg */
|
||||
e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true);
|
||||
e = BM_vert_collapse_faces(
|
||||
bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true, true);
|
||||
|
||||
if (!e) {
|
||||
return false;
|
||||
|
@ -432,7 +433,8 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
|
|||
float fac,
|
||||
const bool do_del,
|
||||
const bool join_faces,
|
||||
const bool kill_degenerate_faces)
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces)
|
||||
{
|
||||
BMEdge *e_new = NULL;
|
||||
BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
|
||||
|
@ -503,7 +505,8 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
|
|||
/* same as BM_vert_collapse_edge() however we already
|
||||
* have vars to perform this operation so don't call. */
|
||||
e_new = bmesh_kernel_join_edge_kill_vert(
|
||||
bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
|
||||
bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, kill_duplicate_faces);
|
||||
|
||||
/* e_new = BM_edge_exists(tv, tv2); */ /* same as return above */
|
||||
}
|
||||
|
||||
|
@ -517,8 +520,12 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
|
|||
*
|
||||
* \return The New Edge
|
||||
*/
|
||||
BMEdge *BM_vert_collapse_edge(
|
||||
BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
|
||||
BMEdge *BM_vert_collapse_edge(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
const bool do_del,
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces)
|
||||
{
|
||||
/* nice example implementation but we want loops to have their customdata
|
||||
* accounted for */
|
||||
|
@ -546,7 +553,8 @@ BMEdge *BM_vert_collapse_edge(
|
|||
#else
|
||||
/* with these args faces are never joined, same as above
|
||||
* but account for loop customdata */
|
||||
return BM_vert_collapse_faces(bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces);
|
||||
return BM_vert_collapse_faces(
|
||||
bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces, kill_duplicate_faces);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,14 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
|
|||
float fac,
|
||||
const bool do_del,
|
||||
const bool join_faces,
|
||||
const bool kill_degenerate_faces);
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces);
|
||||
BMEdge *BM_vert_collapse_edge(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
BMVert *v_kill,
|
||||
const bool do_del,
|
||||
const bool kill_degenerate_faces);
|
||||
const bool kill_degenerate_faces,
|
||||
const bool kill_duplicate_faces);
|
||||
|
||||
BMVert *BM_edge_collapse(BMesh *bm,
|
||||
BMEdge *e_kill,
|
||||
|
|
|
@ -242,7 +242,7 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op)
|
|||
BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
|
||||
if (BM_vert_is_edge_pair(v)) {
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true);
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,6 +327,10 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
|
|||
|
||||
/* join faces */
|
||||
f_new = BM_faces_join_pair(bm, l_a, l_b, false);
|
||||
if (f_new && BM_face_find_double(f_new)) {
|
||||
BM_face_kill(bm, f_new);
|
||||
f_new = NULL;
|
||||
}
|
||||
|
||||
if (f_new) {
|
||||
/* maintain active face */
|
||||
|
@ -355,7 +359,7 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
|
|||
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
|
||||
if (BM_vert_is_edge_pair(v)) {
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true);
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -441,10 +445,16 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
|
|||
|
||||
/* join faces */
|
||||
f_new = BM_faces_join_pair(bm, l_a, l_b, false);
|
||||
if (f_new && BM_face_find_double(f_new)) {
|
||||
BM_face_kill(bm, f_new);
|
||||
f_new = NULL;
|
||||
}
|
||||
|
||||
/* maintain active face */
|
||||
if (act_face && bm->act_face == NULL) {
|
||||
bm->act_face = f_new;
|
||||
if (f_new) {
|
||||
/* maintain active face */
|
||||
if (act_face && bm->act_face == NULL) {
|
||||
bm->act_face = f_new;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,7 +472,7 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
|
|||
/* final cleanup */
|
||||
BMO_ITER (v, &oiter, op->slots_in, "verts", BM_VERT) {
|
||||
if (BM_vert_is_edge_pair(v)) {
|
||||
BM_vert_collapse_edge(bm, v->e, v, false, true);
|
||||
BM_vert_collapse_edge(bm, v->e, v, false, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op)
|
|||
}
|
||||
|
||||
while ((v = STACK_POP(varr))) {
|
||||
bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false);
|
||||
bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -439,7 +439,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
|
|||
for (i = 0; i < vinput_len; i++) {
|
||||
BMVert *v = vinput_arr[i];
|
||||
if (LIKELY(v != NULL) && BM_vert_is_edge_pair(v)) {
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -482,7 +482,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
|
|||
BM_vert_is_edge_pair(v)
|
||||
#endif
|
||||
) {
|
||||
e_new = BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
|
||||
e_new = BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
|
||||
|
||||
if (e_new) {
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ static bool bm_vert_dissolve_fan(BMesh *bm, BMVert *v)
|
|||
if (tot_edge == 2) {
|
||||
/* check for 2 wire verts only */
|
||||
if (tot_edge_wire == 2) {
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
|
||||
return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
|
||||
}
|
||||
}
|
||||
else if (tot_edge == 4) {
|
||||
|
|
|
@ -1622,7 +1622,7 @@ bool BM_mesh_intersect(BMesh *bm,
|
|||
}
|
||||
|
||||
if (ok) {
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, false);
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,11 +59,11 @@ set(SRC
|
|||
intern/COM_CompositorContext.h
|
||||
intern/COM_Converter.cc
|
||||
intern/COM_Converter.h
|
||||
intern/COM_Enums.cc
|
||||
intern/COM_Debug.cc
|
||||
intern/COM_Debug.h
|
||||
intern/COM_Device.cc
|
||||
intern/COM_Device.h
|
||||
intern/COM_Enums.cc
|
||||
intern/COM_ExecutionGroup.cc
|
||||
intern/COM_ExecutionGroup.h
|
||||
intern/COM_ExecutionSystem.cc
|
||||
|
@ -279,6 +279,8 @@ set(SRC
|
|||
nodes/COM_VectorBlurNode.h
|
||||
operations/COM_VectorBlurOperation.cc
|
||||
operations/COM_VectorBlurOperation.h
|
||||
nodes/COM_AntiAliasingNode.cc
|
||||
nodes/COM_AntiAliasingNode.h
|
||||
nodes/COM_BlurNode.cc
|
||||
nodes/COM_BlurNode.h
|
||||
nodes/COM_BokehBlurNode.cc
|
||||
|
@ -295,8 +297,6 @@ set(SRC
|
|||
nodes/COM_FilterNode.h
|
||||
nodes/COM_InpaintNode.cc
|
||||
nodes/COM_InpaintNode.h
|
||||
nodes/COM_AntiAliasingNode.cc
|
||||
nodes/COM_AntiAliasingNode.h
|
||||
|
||||
operations/COM_BlurBaseOperation.cc
|
||||
operations/COM_BlurBaseOperation.h
|
||||
|
@ -322,10 +322,10 @@ set(SRC
|
|||
operations/COM_MovieClipAttributeOperation.h
|
||||
operations/COM_MovieDistortionOperation.cc
|
||||
operations/COM_MovieDistortionOperation.h
|
||||
operations/COM_VariableSizeBokehBlurOperation.cc
|
||||
operations/COM_VariableSizeBokehBlurOperation.h
|
||||
operations/COM_SMAAOperation.cc
|
||||
operations/COM_SMAAOperation.h
|
||||
operations/COM_VariableSizeBokehBlurOperation.cc
|
||||
operations/COM_VariableSizeBokehBlurOperation.h
|
||||
|
||||
# Matte nodes
|
||||
nodes/COM_BoxMaskNode.cc
|
||||
|
|
|
@ -62,15 +62,29 @@ void NodeOperation::addOutputSocket(DataType datatype)
|
|||
void NodeOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
{
|
||||
if (m_resolutionInputSocketIndex < m_inputs.size()) {
|
||||
unsigned int used_resolution_index = 0;
|
||||
if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
|
||||
for (NodeOperationInput &input : m_inputs) {
|
||||
unsigned int any_resolution[2] = {0, 0};
|
||||
input.determineResolution(any_resolution, preferredResolution);
|
||||
if (any_resolution[0] * any_resolution[1] > 0) {
|
||||
resolution[0] = any_resolution[0];
|
||||
resolution[1] = any_resolution[1];
|
||||
break;
|
||||
}
|
||||
used_resolution_index += 1;
|
||||
}
|
||||
}
|
||||
else if (m_resolutionInputSocketIndex < m_inputs.size()) {
|
||||
NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
|
||||
input.determineResolution(resolution, preferredResolution);
|
||||
used_resolution_index = m_resolutionInputSocketIndex;
|
||||
}
|
||||
unsigned int temp2[2] = {resolution[0], resolution[1]};
|
||||
|
||||
unsigned int temp[2];
|
||||
for (unsigned int index = 0; index < m_inputs.size(); index++) {
|
||||
if (index == this->m_resolutionInputSocketIndex) {
|
||||
if (index == used_resolution_index) {
|
||||
continue;
|
||||
}
|
||||
NodeOperationInput &input = m_inputs[index];
|
||||
|
@ -206,7 +220,9 @@ void NodeOperationOutput::determineResolution(unsigned int resolution[2],
|
|||
}
|
||||
else {
|
||||
operation.determineResolution(resolution, preferredResolution);
|
||||
operation.setResolution(resolution);
|
||||
if (resolution[0] > 0 && resolution[1] > 0) {
|
||||
operation.setResolution(resolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,13 @@ class WriteBufferOperation;
|
|||
class NodeOperation;
|
||||
typedef NodeOperation SocketReader;
|
||||
|
||||
/**
|
||||
* RESOLUTION_INPUT_ANY is a wildcard when any resolution of an input can be used.
|
||||
* This solves the issue that the FileInputNode in a group node cannot find the
|
||||
* correct resolution.
|
||||
*/
|
||||
static constexpr unsigned int RESOLUTION_INPUT_ANY = 999999;
|
||||
|
||||
/**
|
||||
* \brief Resize modes of inputsockets
|
||||
* How are the input and working resolutions matched
|
||||
|
|
|
@ -63,7 +63,7 @@ void GlareNode::convertToOperations(NodeConverter &converter,
|
|||
thresholdOperation->setGlareSettings(glare);
|
||||
|
||||
SetValueOperation *mixvalueoperation = new SetValueOperation();
|
||||
mixvalueoperation->setValue(0.5f + glare->mix * 0.5f);
|
||||
mixvalueoperation->setValue(glare->mix);
|
||||
|
||||
MixGlareOperation *mixoperation = new MixGlareOperation();
|
||||
mixoperation->setResolutionInputSocketIndex(1);
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include "COM_OutputFileNode.h"
|
||||
#include "COM_ExecutionSystem.h"
|
||||
#include "COM_OutputFileMultiViewOperation.h"
|
||||
#include "COM_OutputFileOperation.h"
|
||||
|
||||
#include "BKE_scene.h"
|
||||
|
@ -32,6 +31,31 @@ OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode)
|
|||
/* pass */
|
||||
}
|
||||
|
||||
void OutputFileNode::add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const
|
||||
{
|
||||
for (NodeInput *input : inputs) {
|
||||
NodeImageMultiFileSocket *sockdata =
|
||||
(NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
|
||||
/* note: layer becomes an empty placeholder if the input is not linked */
|
||||
operation.add_layer(sockdata->layer, input->getDataType(), input->isLinked());
|
||||
}
|
||||
}
|
||||
|
||||
void OutputFileNode::map_input_sockets(NodeConverter &converter,
|
||||
OutputOpenExrMultiLayerOperation &operation) const
|
||||
{
|
||||
bool previewAdded = false;
|
||||
int index = 0;
|
||||
for (NodeInput *input : inputs) {
|
||||
converter.mapInputSocket(input, operation.getInputSocket(index++));
|
||||
|
||||
if (!previewAdded) {
|
||||
converter.addNodeInputPreview(input);
|
||||
previewAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OutputFileNode::convertToOperations(NodeConverter &converter,
|
||||
const CompositorContext &context) const
|
||||
{
|
||||
|
@ -71,22 +95,11 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
|
|||
}
|
||||
converter.addOperation(outputOperation);
|
||||
|
||||
bool previewAdded = false;
|
||||
int index = 0;
|
||||
for (NodeInput *input : inputs) {
|
||||
NodeImageMultiFileSocket *sockdata =
|
||||
(NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
|
||||
|
||||
/* note: layer becomes an empty placeholder if the input is not linked */
|
||||
outputOperation->add_layer(sockdata->layer, input->getDataType(), input->isLinked());
|
||||
|
||||
converter.mapInputSocket(input, outputOperation->getInputSocket(index++));
|
||||
|
||||
if (!previewAdded) {
|
||||
converter.addNodeInputPreview(input);
|
||||
previewAdded = true;
|
||||
}
|
||||
}
|
||||
/* First add all inputs. Inputs are stored in a Vector and can be moved to a different
|
||||
* memory address during this time.*/
|
||||
add_input_sockets(*outputOperation);
|
||||
/* After adding the sockets the memory addresses will stick. */
|
||||
map_input_sockets(converter, *outputOperation);
|
||||
}
|
||||
else { /* single layer format */
|
||||
bool previewAdded = false;
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "COM_Node.h"
|
||||
|
||||
#include "COM_OutputFileMultiViewOperation.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
@ -32,6 +35,11 @@ class OutputFileNode : public Node {
|
|||
OutputFileNode(bNode *editorNode);
|
||||
void convertToOperations(NodeConverter &converter,
|
||||
const CompositorContext &context) const override;
|
||||
|
||||
private:
|
||||
void add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const;
|
||||
void map_input_sockets(NodeConverter &converter,
|
||||
OutputOpenExrMultiLayerOperation &operation) const;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
|
@ -101,6 +101,11 @@ void DenoiseOperation::generateDenoise(float *data,
|
|||
if (BLI_cpu_support_sse41())
|
||||
# endif
|
||||
{
|
||||
/* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
|
||||
* OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
|
||||
*/
|
||||
BLI_mutex_lock(&oidn_lock);
|
||||
|
||||
oidn::DeviceRef device = oidn::newDevice();
|
||||
device.commit();
|
||||
|
||||
|
@ -145,10 +150,6 @@ void DenoiseOperation::generateDenoise(float *data,
|
|||
}
|
||||
|
||||
filter.commit();
|
||||
/* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
|
||||
* OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
|
||||
*/
|
||||
BLI_mutex_lock(&oidn_lock);
|
||||
filter.execute();
|
||||
BLI_mutex_unlock(&oidn_lock);
|
||||
|
||||
|
|
|
@ -462,27 +462,26 @@ void MixGlareOperation::executePixelSampled(float output[4],
|
|||
float inputColor1[4];
|
||||
float inputColor2[4];
|
||||
float inputValue[4];
|
||||
float value;
|
||||
float value, input_weight, glare_weight;
|
||||
|
||||
this->m_inputValueOperation->readSampled(inputValue, x, y, sampler);
|
||||
this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler);
|
||||
this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler);
|
||||
value = inputValue[0];
|
||||
float mf = 2.0f - 2.0f * fabsf(value - 0.5f);
|
||||
|
||||
if (inputColor1[0] < 0.0f) {
|
||||
inputColor1[0] = 0.0f;
|
||||
/* Linear interpolation between 3 cases:
|
||||
* value=-1:output=input value=0:output=input+glare value=1:output=glare
|
||||
*/
|
||||
if (value < 0.0f) {
|
||||
input_weight = 1.0f;
|
||||
glare_weight = 1.0f + value;
|
||||
}
|
||||
if (inputColor1[1] < 0.0f) {
|
||||
inputColor1[1] = 0.0f;
|
||||
else {
|
||||
input_weight = 1.0f - value;
|
||||
glare_weight = 1.0f;
|
||||
}
|
||||
if (inputColor1[2] < 0.0f) {
|
||||
inputColor1[2] = 0.0f;
|
||||
}
|
||||
|
||||
output[0] = mf * MAX2(inputColor1[0] + value * (inputColor2[0] - inputColor1[0]), 0.0f);
|
||||
output[1] = mf * MAX2(inputColor1[1] + value * (inputColor2[1] - inputColor1[1]), 0.0f);
|
||||
output[2] = mf * MAX2(inputColor1[2] + value * (inputColor2[2] - inputColor1[2]), 0.0f);
|
||||
output[0] = input_weight * MAX2(inputColor1[0], 0.0f) + glare_weight * inputColor2[0];
|
||||
output[1] = input_weight * MAX2(inputColor1[1], 0.0f) + glare_weight * inputColor2[1];
|
||||
output[2] = input_weight * MAX2(inputColor1[2], 0.0f) + glare_weight * inputColor2[2];
|
||||
output[3] = inputColor1[3];
|
||||
|
||||
clampIfNeeded(output);
|
||||
|
|
|
@ -323,6 +323,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *
|
|||
this->m_exr_codec = exr_codec;
|
||||
this->m_exr_half_float = exr_half_float;
|
||||
this->m_viewName = viewName;
|
||||
this->setResolutionInputSocketIndex(RESOLUTION_INPUT_ANY);
|
||||
}
|
||||
|
||||
void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
|
||||
|
|
|
@ -105,10 +105,12 @@ static void gpencil_shade_color(float color[3])
|
|||
}
|
||||
|
||||
/* Apply all overrides from the solid viewport mode to the GPencil material. */
|
||||
static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_PrivateData *pd,
|
||||
Object *ob,
|
||||
int color_type,
|
||||
MaterialGPencilStyle *gp_style)
|
||||
static MaterialGPencilStyle *gpencil_viewport_material_overrides(
|
||||
GPENCIL_PrivateData *pd,
|
||||
Object *ob,
|
||||
int color_type,
|
||||
MaterialGPencilStyle *gp_style,
|
||||
const eV3DShadingLightingMode lighting_mode)
|
||||
{
|
||||
static MaterialGPencilStyle gp_style_tmp;
|
||||
|
||||
|
@ -148,7 +150,9 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_Private
|
|||
copy_v3_v3(gp_style->fill_rgba, pd->v3d_single_color);
|
||||
gp_style->fill_rgba[3] = 1.0f;
|
||||
copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba);
|
||||
gpencil_shade_color(gp_style->stroke_rgba);
|
||||
if (lighting_mode != V3D_LIGHTING_FLAT) {
|
||||
gpencil_shade_color(gp_style->fill_rgba);
|
||||
}
|
||||
break;
|
||||
case V3D_SHADING_OBJECT_COLOR:
|
||||
gp_style = &gp_style_tmp;
|
||||
|
@ -156,7 +160,9 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_Private
|
|||
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v4_v4(gp_style->fill_rgba, ob->color);
|
||||
copy_v4_v4(gp_style->stroke_rgba, ob->color);
|
||||
gpencil_shade_color(gp_style->stroke_rgba);
|
||||
if (lighting_mode != V3D_LIGHTING_FLAT) {
|
||||
gpencil_shade_color(gp_style->fill_rgba);
|
||||
}
|
||||
break;
|
||||
case V3D_SHADING_VERTEX_COLOR:
|
||||
gp_style = &gp_style_tmp;
|
||||
|
@ -198,6 +204,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
|
|||
int color_type = (pd->v3d_color_type != -1 && GPENCIL_VERTEX_MODE(gpd)) ?
|
||||
V3D_SHADING_VERTEX_COLOR :
|
||||
pd->v3d_color_type;
|
||||
const eV3DShadingLightingMode lighting_mode = (pd->v3d != NULL) ? pd->v3d->shading.light :
|
||||
V3D_LIGHTING_STUDIO;
|
||||
|
||||
GPENCIL_MaterialPool *pool = matpool;
|
||||
for (int i = 0; i < mat_len; i++) {
|
||||
|
@ -245,7 +253,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
|
|||
mat_data->flag |= GP_FILL_HOLDOUT;
|
||||
}
|
||||
|
||||
gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style);
|
||||
gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style, lighting_mode);
|
||||
|
||||
/* Dots or Squares rotation. */
|
||||
mat_data->alignment_rot_cos = cosf(gp_style->alignment_rotation);
|
||||
|
|
|
@ -2141,7 +2141,7 @@ static int armature_select_mirror_exec(bContext *C, wmOperator *op)
|
|||
void ARMATURE_OT_select_mirror(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Flip Active/Selected Bone";
|
||||
ot->name = "Select Mirror";
|
||||
ot->idname = "ARMATURE_OT_select_mirror";
|
||||
ot->description = "Mirror the bone selection";
|
||||
|
||||
|
|
|
@ -1284,7 +1284,7 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
|
|||
void POSE_OT_select_mirror(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Flip Active/Selected Bone";
|
||||
ot->name = "Select Mirror";
|
||||
ot->idname = "POSE_OT_select_mirror";
|
||||
ot->description = "Mirror the bone selection";
|
||||
|
||||
|
|
|
@ -3981,6 +3981,11 @@ static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
|
|||
}
|
||||
}
|
||||
|
||||
if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) &&
|
||||
(gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) {
|
||||
totnewpoints++;
|
||||
}
|
||||
|
||||
return totnewpoints;
|
||||
}
|
||||
|
||||
|
@ -4079,6 +4084,47 @@ static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Subdivide between last and first point. */
|
||||
if (gps->flag & GP_STROKE_CYCLIC) {
|
||||
bGPDspoint *pt = &temp_points[oldtotpoints - 1];
|
||||
bGPDspoint *next = &temp_points[0];
|
||||
if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) {
|
||||
bGPDspoint *pt_final = &gps->points[i2];
|
||||
if (gps->dvert != NULL) {
|
||||
dvert_final = &gps->dvert[i2];
|
||||
}
|
||||
/* Interpolate all values */
|
||||
interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
|
||||
pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
|
||||
pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
|
||||
CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
|
||||
interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
|
||||
pt_final->time = interpf(pt->time, next->time, 0.5f);
|
||||
pt_final->flag |= GP_SPOINT_SELECT;
|
||||
|
||||
/* interpolate weights */
|
||||
if (gps->dvert != NULL) {
|
||||
dvert = &temp_dverts[oldtotpoints - 1];
|
||||
dvert_next = &temp_dverts[0];
|
||||
dvert_final = &gps->dvert[i2];
|
||||
|
||||
dvert_final->totweight = dvert->totweight;
|
||||
dvert_final->dw = MEM_dupallocN(dvert->dw);
|
||||
|
||||
/* interpolate weight values */
|
||||
for (int d = 0; d < dvert->totweight; d++) {
|
||||
MDeformWeight *dw_a = &dvert->dw[d];
|
||||
if (dvert_next->totweight > d) {
|
||||
MDeformWeight *dw_b = &dvert_next->dw[d];
|
||||
MDeformWeight *dw_final = &dvert_final->dw[d];
|
||||
dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free temp memory */
|
||||
MEM_SAFE_FREE(temp_points);
|
||||
MEM_SAFE_FREE(temp_dverts);
|
||||
|
|
|
@ -505,7 +505,7 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot)
|
|||
/* properties */
|
||||
RNA_def_boolean(ot->srna,
|
||||
"unselect_ends",
|
||||
true,
|
||||
false,
|
||||
"Unselect Ends",
|
||||
"Do not select the first and last point of the stroke");
|
||||
}
|
||||
|
|
|
@ -107,8 +107,9 @@ bool user_string_to_number(bContext *C,
|
|||
const char *str,
|
||||
const struct UnitSettings *unit,
|
||||
int type,
|
||||
const char *error_prefix,
|
||||
double *r_value);
|
||||
double *r_value,
|
||||
const bool use_single_line_error,
|
||||
char **r_error);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -91,8 +91,7 @@ void ED_preview_shader_job(const struct bContext *C,
|
|||
int sizex,
|
||||
int sizey,
|
||||
int method);
|
||||
void ED_preview_icon_render(struct Main *bmain,
|
||||
struct Depsgraph *depsgraph,
|
||||
void ED_preview_icon_render(const struct bContext *C,
|
||||
struct Scene *scene,
|
||||
struct ID *id,
|
||||
unsigned int *rect,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct SpreadsheetContext;
|
||||
struct SpaceSpreadsheet;
|
||||
struct SpaceNode;
|
||||
struct ID;
|
||||
struct bNode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct SpreadsheetContext *ED_spreadsheet_context_new(int type);
|
||||
void ED_spreadsheet_context_free(struct SpreadsheetContext *context);
|
||||
void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet);
|
||||
void ED_spreadsheet_context_path_update_tag(struct SpaceSpreadsheet *sspreadsheet);
|
||||
uint64_t ED_spreadsheet_context_path_hash(struct SpaceSpreadsheet *sspreadsheet);
|
||||
|
||||
struct ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet);
|
||||
|
||||
void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
|
||||
struct SpaceNode *snode,
|
||||
struct bNode *node);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1600,6 +1600,7 @@ void UI_but_func_search_set(uiBut *but,
|
|||
uiButSearchCreateFn search_create_fn,
|
||||
uiButSearchUpdateFn search_update_fn,
|
||||
void *arg,
|
||||
const bool free_arg,
|
||||
uiButSearchArgFreeFn search_arg_free_fn,
|
||||
uiButHandleFunc search_exec_fn,
|
||||
void *active);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "BKE_context.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_unit.h"
|
||||
|
@ -2912,7 +2913,14 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
|
|||
static bool ui_number_from_string_units(
|
||||
bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value)
|
||||
{
|
||||
return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
|
||||
char *error = NULL;
|
||||
const bool ok = user_string_to_number(C, str, unit, unit_type, r_value, true, &error);
|
||||
if (error) {
|
||||
ReportList *reports = CTX_wm_reports(C);
|
||||
BKE_reportf(reports, RPT_ERROR, "%s: %s", UI_NUMBER_EVAL_ERROR_PREFIX, error);
|
||||
MEM_freeN(error);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool ui_number_from_string_units_with_but(bContext *C,
|
||||
|
@ -2929,7 +2937,11 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
|
|||
{
|
||||
bool ok;
|
||||
#ifdef WITH_PYTHON
|
||||
ok = BPY_run_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
|
||||
struct BPy_RunErrInfo err_info = {
|
||||
.reports = CTX_wm_reports(C),
|
||||
.report_prefix = UI_NUMBER_EVAL_ERROR_PREFIX,
|
||||
};
|
||||
ok = BPY_run_string_as_number(C, NULL, str, &err_info, r_value);
|
||||
#else
|
||||
UNUSED_VARS(C);
|
||||
*r_value = atof(str);
|
||||
|
@ -6590,6 +6602,8 @@ uiBut *uiDefSearchBut(uiBlock *block,
|
|||
* \param search_create_fn: Function to create the menu.
|
||||
* \param search_update_fn: Function to refresh search content after the search text has changed.
|
||||
* \param arg: user value.
|
||||
* \param free_arg: Set to true if the argument is newly allocated memory for every redraw and
|
||||
* should be freed when the button is destroyed.
|
||||
* \param search_arg_free_fn: When non-null, use this function to free \a arg.
|
||||
* \param search_exec_fn: Function that executes the action, gets \a arg as the first argument.
|
||||
* The second argument as the active item-pointer
|
||||
|
@ -6600,6 +6614,7 @@ void UI_but_func_search_set(uiBut *but,
|
|||
uiButSearchCreateFn search_create_fn,
|
||||
uiButSearchUpdateFn search_update_fn,
|
||||
void *arg,
|
||||
const bool free_arg,
|
||||
uiButSearchArgFreeFn search_arg_free_fn,
|
||||
uiButHandleFunc search_exec_fn,
|
||||
void *active)
|
||||
|
@ -6635,11 +6650,17 @@ void UI_but_func_search_set(uiBut *but,
|
|||
}
|
||||
#endif
|
||||
/* Handling will pass the active item as arg2 later, so keep it NULL here. */
|
||||
UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
|
||||
if (free_arg) {
|
||||
UI_but_funcN_set(but, search_exec_fn, search_but->arg, NULL);
|
||||
}
|
||||
else {
|
||||
UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* search buttons show red-alert if item doesn't exist, not for menus */
|
||||
if (0 == (but->block->flag & UI_BLOCK_LOOP)) {
|
||||
/* search buttons show red-alert if item doesn't exist, not for menus. Don't do this for
|
||||
* buttons where any result is valid anyway, since any string will be valid anyway. */
|
||||
if (0 == (but->block->flag & UI_BLOCK_LOOP) && !search_but->results_are_suggestions) {
|
||||
/* skip empty buttons, not all buttons need input, we only show invalid */
|
||||
if (but->drawstr[0]) {
|
||||
ui_but_search_refresh(search_but);
|
||||
|
@ -6779,6 +6800,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
|
|||
ui_searchbox_create_generic,
|
||||
operator_enum_search_update_fn,
|
||||
but,
|
||||
false,
|
||||
NULL,
|
||||
operator_enum_search_exec_fn,
|
||||
NULL);
|
||||
|
|
|
@ -399,7 +399,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
|
|||
"'%s').label",
|
||||
idname);
|
||||
char *expr_result = NULL;
|
||||
if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
|
||||
if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
|
||||
STRNCPY(drawstr, expr_result);
|
||||
MEM_freeN(expr_result);
|
||||
}
|
||||
|
|
|
@ -1428,13 +1428,7 @@ static void icon_set_image(const bContext *C,
|
|||
scene = CTX_data_scene(C);
|
||||
}
|
||||
/* Immediate version */
|
||||
ED_preview_icon_render(CTX_data_main(C),
|
||||
CTX_data_ensure_evaluated_depsgraph(C),
|
||||
scene,
|
||||
id,
|
||||
prv_img->rect[size],
|
||||
prv_img->w[size],
|
||||
prv_img->h[size]);
|
||||
ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2718,6 +2718,7 @@ uiBut *ui_but_add_search(
|
|||
ui_searchbox_create_generic,
|
||||
ui_rna_collection_search_update_fn,
|
||||
coll_search,
|
||||
false,
|
||||
ui_rna_collection_search_arg_free_fn,
|
||||
NULL,
|
||||
NULL);
|
||||
|
|
|
@ -428,7 +428,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
if (has_valid_context == false) {
|
||||
expr_result = BLI_strdup(has_valid_context_error);
|
||||
}
|
||||
else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
|
||||
else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
|
||||
if (STREQ(expr_result, "")) {
|
||||
MEM_freeN(expr_result);
|
||||
expr_result = NULL;
|
||||
|
@ -485,7 +485,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
if (has_valid_context == false) {
|
||||
expr_result = BLI_strdup(has_valid_context_error);
|
||||
}
|
||||
else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
|
||||
else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
|
||||
if (STREQ(expr_result, ".")) {
|
||||
MEM_freeN(expr_result);
|
||||
expr_result = NULL;
|
||||
|
@ -589,7 +589,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
if (has_valid_context == false) {
|
||||
shortcut = BLI_strdup(has_valid_context_error);
|
||||
}
|
||||
else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
|
||||
else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) {
|
||||
if (expr_result != 0) {
|
||||
wmKeyMap *keymap = (wmKeyMap *)expr_result;
|
||||
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
|
||||
|
@ -654,7 +654,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
/* pass */
|
||||
}
|
||||
else if (BPY_run_string_as_string_and_size(
|
||||
C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) {
|
||||
C, expr_imports, expr, NULL, &expr_result, &expr_result_len)) {
|
||||
/* pass. */
|
||||
}
|
||||
}
|
||||
|
@ -731,7 +731,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
|||
if (has_valid_context == false) {
|
||||
/* pass */
|
||||
}
|
||||
else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
|
||||
else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) {
|
||||
if (expr_result != 0) {
|
||||
{
|
||||
uiTooltipField *field = text_field_add(data,
|
||||
|
|
|
@ -1148,6 +1148,7 @@ void UI_but_func_menu_search(uiBut *but)
|
|||
ui_searchbox_create_menu,
|
||||
menu_search_update_fn,
|
||||
data,
|
||||
false,
|
||||
menu_search_arg_free_fn,
|
||||
menu_search_exec_fn,
|
||||
NULL);
|
||||
|
|
|
@ -121,6 +121,7 @@ void UI_but_func_operator_search(uiBut *but)
|
|||
operator_search_update_fn,
|
||||
NULL,
|
||||
false,
|
||||
NULL,
|
||||
operator_search_exec_fn,
|
||||
NULL);
|
||||
}
|
||||
|
|
|
@ -309,6 +309,7 @@ static uiBlock *template_common_search_menu(const bContext *C,
|
|||
ui_searchbox_create_generic,
|
||||
search_update_fn,
|
||||
search_arg,
|
||||
false,
|
||||
NULL,
|
||||
search_exec_fn,
|
||||
active_item);
|
||||
|
|
|
@ -1225,12 +1225,6 @@ static bool draw_widgetbase_batch_skip_draw_cache(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* There are also reports that some AMD and Mesa driver configuration suffer from the
|
||||
* same issue, T78803. */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
struct ARegion;
|
||||
struct bContext;
|
||||
struct View3D;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
|
||||
void WM_OT_gpencil_import_svg(struct wmOperatorType *ot);
|
||||
|
|
|
@ -90,7 +90,7 @@ void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot);
|
|||
void OBJECT_OT_move_to_collection(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_link_to_collection(struct wmOperatorType *ot);
|
||||
|
||||
void OBJECT_OT_switch_object(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_transfer_mode(struct wmOperatorType *ot);
|
||||
|
||||
/* object_select.c */
|
||||
void OBJECT_OT_select_all(struct wmOperatorType *ot);
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "GPU_immediate_util.h"
|
||||
#include "GPU_matrix.h"
|
||||
#include "GPU_state.h"
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_gpencil_modifier.h"
|
||||
|
@ -52,6 +53,7 @@
|
|||
#include "BKE_paint.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
@ -416,7 +418,7 @@ bool ED_object_mode_generic_has_data(struct Depsgraph *depsgraph, struct Object
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Switch Object
|
||||
/** \name Transfer Mode
|
||||
*
|
||||
* Enters the same mode of the current active object in another object,
|
||||
* leaving the mode of the current object.
|
||||
|
@ -434,7 +436,7 @@ typedef struct SwitchObjectCustomData {
|
|||
float alpha;
|
||||
} SwitchObjectCustomData;
|
||||
|
||||
static void switch_object_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
|
||||
static void transfer_mode_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
|
||||
{
|
||||
SwitchObjectCustomData *cd = arg;
|
||||
|
||||
|
@ -478,73 +480,53 @@ static void switch_object_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), v
|
|||
GPU_line_smooth(false);
|
||||
}
|
||||
|
||||
static int object_switch_object_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
static bool object_transfer_mode_poll(bContext *C)
|
||||
{
|
||||
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
SwitchObjectCustomData *cd = op->customdata;
|
||||
if (cd->start_time == 0.0) {
|
||||
cd->start_time = PIL_check_seconds_timer();
|
||||
}
|
||||
const double time = PIL_check_seconds_timer();
|
||||
const double delta_time = time - cd->start_time;
|
||||
float alpha = 1.0f - (6.0 * delta_time);
|
||||
|
||||
if (alpha > 0.01f) {
|
||||
cd->alpha = alpha;
|
||||
ED_region_tag_redraw(region);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), cd->timer);
|
||||
|
||||
ED_region_draw_cb_exit(region->type, cd->draw_handle);
|
||||
MEM_freeN(op->customdata);
|
||||
ED_region_tag_redraw(region);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool object_switch_object_poll(bContext *C)
|
||||
{
|
||||
|
||||
if (!U.experimental.use_switch_object_operator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CTX_wm_region_view3d(C)) {
|
||||
return false;
|
||||
}
|
||||
const Object *ob = CTX_data_active_object(C);
|
||||
return ob && (ob->mode & (OB_MODE_EDIT | OB_MODE_SCULPT));
|
||||
return ob && (ob->mode & (OB_MODE_SCULPT));
|
||||
}
|
||||
|
||||
static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
/* Update the viewport rotation origin to the mouse cursor. */
|
||||
static void object_transfer_mode_reposition_view_pivot(bContext *C, const int mval[2])
|
||||
{
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
float global_loc[3];
|
||||
if (!ED_view3d_autodist_simple(region, mval, global_loc, 0, NULL)) {
|
||||
return;
|
||||
}
|
||||
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
||||
copy_v3_v3(ups->average_stroke_accum, global_loc);
|
||||
ups->average_stroke_counter = 1;
|
||||
ups->last_stroke_valid = true;
|
||||
}
|
||||
|
||||
static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base_dst)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
|
||||
|
||||
if (base_dst == NULL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *ob_dst = base_dst->object;
|
||||
Object *ob_src = CTX_data_active_object(C);
|
||||
|
||||
if (ob_dst == ob_src) {
|
||||
return OPERATOR_CANCELLED;
|
||||
return false;
|
||||
}
|
||||
|
||||
const eObjectMode last_mode = (eObjectMode)ob_src->mode;
|
||||
if (!ED_object_mode_compat_test(ob_dst, last_mode)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
return false;
|
||||
}
|
||||
|
||||
int retval = OPERATOR_CANCELLED;
|
||||
bool switched = false;
|
||||
bool mode_transfered = false;
|
||||
|
||||
ED_undo_group_begin(C);
|
||||
|
||||
|
@ -560,43 +542,64 @@ static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEven
|
|||
ob_dst_orig = DEG_get_original_object(ob_dst);
|
||||
ED_object_mode_set_ex(C, last_mode, true, op->reports);
|
||||
|
||||
/* Update the viewport rotation origin to the mouse cursor. */
|
||||
if (last_mode & OB_MODE_ALL_PAINT) {
|
||||
float global_loc[3];
|
||||
if (ED_view3d_autodist_simple(region, event->mval, global_loc, 0, NULL)) {
|
||||
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
||||
copy_v3_v3(ups->average_stroke_accum, global_loc);
|
||||
ups->average_stroke_counter = 1;
|
||||
ups->last_stroke_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
|
||||
WM_toolsystem_update_from_context_view3d(C);
|
||||
retval = OPERATOR_FINISHED;
|
||||
switched = true;
|
||||
mode_transfered = true;
|
||||
}
|
||||
|
||||
ED_undo_group_end(C);
|
||||
return mode_transfered;
|
||||
}
|
||||
|
||||
const bool flash_object = true;
|
||||
static bool object_transfer_mode_flash_animation_update(bContext *C, wmOperator *op) {
|
||||
SwitchObjectCustomData *cd = op->customdata;
|
||||
|
||||
if (!flash_object) {
|
||||
if (switched) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (cd == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
if (cd->start_time == 0.0) {
|
||||
cd->start_time = PIL_check_seconds_timer();
|
||||
}
|
||||
const double time = PIL_check_seconds_timer();
|
||||
const double delta_time = time - cd->start_time;
|
||||
float alpha = 1.0f - (6.0 * delta_time);
|
||||
|
||||
if (alpha > 0.01f) {
|
||||
cd->alpha = alpha;
|
||||
ED_region_tag_redraw(region);
|
||||
return false;
|
||||
}
|
||||
|
||||
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), cd->timer);
|
||||
|
||||
ED_region_draw_cb_exit(region->type, cd->draw_handle);
|
||||
MEM_freeN(op->customdata);
|
||||
ED_region_tag_redraw(region);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void object_transfer_mode_flash_animation_begin(bContext *C, wmOperator *op, Base *base_dst) {
|
||||
|
||||
if (!base_dst) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!base_dst->object) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
SwitchObjectCustomData *cd = MEM_callocN(sizeof(SwitchObjectCustomData),
|
||||
"Switch Object Custom Data");
|
||||
|
||||
cd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.05f);
|
||||
cd->draw_handle = ED_region_draw_cb_activate(
|
||||
region->type, switch_object_draw, cd, REGION_DRAW_POST_VIEW);
|
||||
cd->target_object = ob_dst;
|
||||
region->type, transfer_mode_draw, cd, REGION_DRAW_POST_VIEW);
|
||||
cd->target_object = base_dst->object;
|
||||
cd->start_time = 0.0;
|
||||
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
|
@ -607,31 +610,123 @@ static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEven
|
|||
cd->tottris = BKE_mesh_runtime_looptri_len(mesh);
|
||||
cd->mesh = mesh;
|
||||
cd->alpha = 1.0f;
|
||||
|
||||
op->customdata = cd;
|
||||
}
|
||||
|
||||
static int object_transfer_mode_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const bool use_eyedropper = RNA_boolean_get(op->ptr, "use_eyedropper");
|
||||
const bool flash_object = RNA_boolean_get(op->ptr, "flash_object");
|
||||
|
||||
if (use_eyedropper) {
|
||||
switch (event->type) {
|
||||
case LEFTMOUSE:
|
||||
if (event->val == KM_PRESS) {
|
||||
WM_cursor_modal_restore(CTX_wm_window(C));
|
||||
ED_workspace_status_text(C, NULL);
|
||||
|
||||
/* This ensures that the click was done in an viewport region. */
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
ARegion *region = BKE_screen_find_main_region_at_xy(
|
||||
screen, SPACE_VIEW3D, event->x, event->y);
|
||||
if (!region) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin};
|
||||
Base *base_dst = ED_view3d_give_base_under_cursor(C, mval);
|
||||
const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
|
||||
if (!mode_transfered) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (!flash_object) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
object_transfer_mode_flash_animation_begin(C, op, base_dst);
|
||||
|
||||
}
|
||||
break;
|
||||
case RIGHTMOUSE: {
|
||||
WM_cursor_modal_restore(CTX_wm_window(C));
|
||||
ED_workspace_status_text(C, NULL);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
if (!flash_object) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!object_transfer_mode_flash_animation_update(C, op)) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const bool use_eyedropper = RNA_boolean_get(op->ptr, "use_eyedropper");
|
||||
const bool flash_object = RNA_boolean_get(op->ptr, "flash_object");
|
||||
|
||||
if (use_eyedropper) {
|
||||
ED_workspace_status_text(C, TIP_("Click in the viewport to select an object"));
|
||||
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER);
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
Object *ob_src = CTX_data_active_object(C);
|
||||
const eObjectMode src_mode = (eObjectMode)ob_src->mode;
|
||||
|
||||
Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
|
||||
const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
|
||||
if (!mode_transfered) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (src_mode & OB_MODE_ALL_PAINT) {
|
||||
object_transfer_mode_reposition_view_pivot(C, event->mval);
|
||||
}
|
||||
|
||||
|
||||
if (!flash_object) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
object_transfer_mode_flash_animation_begin(C, op, base_dst);
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
void OBJECT_OT_switch_object(wmOperatorType *ot)
|
||||
void OBJECT_OT_transfer_mode(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Switch Object";
|
||||
ot->idname = "OBJECT_OT_switch_object";
|
||||
ot->name = "Transfer Mode";
|
||||
ot->idname = "OBJECT_OT_transfer_mode";
|
||||
ot->description =
|
||||
"Switches the active object and assigns the same mode to a new one under the mouse cursor, "
|
||||
"leaving the active mode in the current one";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = object_switch_object_invoke;
|
||||
ot->modal = object_switch_object_modal;
|
||||
ot->poll = object_switch_object_poll;
|
||||
ot->invoke = object_transfer_mode_invoke;
|
||||
ot->modal = object_transfer_mode_modal;
|
||||
ot->poll = object_transfer_mode_poll;
|
||||
|
||||
/* Undo push is handled by the operator. */
|
||||
ot->flag = OPTYPE_REGISTER;
|
||||
|
||||
RNA_def_boolean(
|
||||
ot->srna, "flash_object", true, "Flash Object", "Flash object when entering the new mode");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_eyedropper",
|
||||
false,
|
||||
"Use Eyedropper",
|
||||
"Pick the object to switch to using an eyedropper");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -65,7 +65,7 @@ void ED_operatortypes_object(void)
|
|||
WM_operatortype_append(OBJECT_OT_paths_range_update);
|
||||
WM_operatortype_append(OBJECT_OT_forcefield_toggle);
|
||||
|
||||
WM_operatortype_append(OBJECT_OT_switch_object);
|
||||
WM_operatortype_append(OBJECT_OT_transfer_mode);
|
||||
|
||||
WM_operatortype_append(OBJECT_OT_parent_set);
|
||||
WM_operatortype_append(OBJECT_OT_parent_no_inverse_set);
|
||||
|
|
|
@ -1608,7 +1608,7 @@ static void icon_preview_free(void *customdata)
|
|||
}
|
||||
|
||||
void ED_preview_icon_render(
|
||||
Main *bmain, Depsgraph *depsgraph, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
|
||||
const bContext *C, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
|
||||
{
|
||||
IconPreview ip = {NULL};
|
||||
short stop = false, update = false;
|
||||
|
@ -1616,9 +1616,9 @@ void ED_preview_icon_render(
|
|||
|
||||
ED_preview_ensure_dbase();
|
||||
|
||||
ip.bmain = bmain;
|
||||
ip.bmain = CTX_data_main(C);
|
||||
ip.scene = scene;
|
||||
ip.depsgraph = depsgraph;
|
||||
ip.depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
ip.owner = BKE_previewimg_id_ensure(id);
|
||||
ip.id = id;
|
||||
/* Control isn't given back to the caller until the preview is done. So we don't need to copy
|
||||
|
|
|
@ -83,7 +83,6 @@ static void draw_render_info(
|
|||
const bContext *C, Scene *scene, Image *ima, ARegion *region, float zoomx, float zoomy)
|
||||
{
|
||||
Render *re = RE_GetSceneRender(scene);
|
||||
RenderData *rd = RE_engine_get_render_data(re);
|
||||
Scene *stats_scene = ED_render_job_get_scene(C);
|
||||
if (stats_scene == NULL) {
|
||||
stats_scene = CTX_data_scene(C);
|
||||
|
@ -112,6 +111,7 @@ static void draw_render_info(
|
|||
GPU_matrix_translate_2f(x, y);
|
||||
GPU_matrix_scale_2f(zoomx, zoomy);
|
||||
|
||||
RenderData *rd = RE_engine_get_render_data(re);
|
||||
if (rd->mode & R_BORDER) {
|
||||
/* TODO: round or floor instead of casting to int */
|
||||
GPU_matrix_translate_2f((int)(-rd->border.xmin * rd->xsch * rd->size * 0.01f),
|
||||
|
|
|
@ -339,7 +339,25 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
|
|||
if (!node_group) {
|
||||
return NULL;
|
||||
}
|
||||
if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group)) {
|
||||
|
||||
const char *disabled_hint = NULL;
|
||||
if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) {
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Can not add node group '%s' to '%s':\n %s",
|
||||
node_group->id.name + 2,
|
||||
ntree->id.name + 2,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Can not add node group '%s' to '%s'",
|
||||
node_group->id.name + 2,
|
||||
ntree->id.name + 2);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -157,6 +157,11 @@ static void draw_socket_list(const bContext *C,
|
|||
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, socket, &socket_ptr);
|
||||
uiItemR(layout, &socket_ptr, "name", 0, NULL, ICON_NONE);
|
||||
|
||||
/* Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. */
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
uiItemR(layout, &socket_ptr, "description", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
if (socket->typeinfo->interface_draw) {
|
||||
socket->typeinfo->interface_draw((bContext *)C, layout, &socket_ptr);
|
||||
}
|
||||
|
|
|
@ -1422,7 +1422,7 @@ static void node_draw_basis(const bContext *C,
|
|||
0,
|
||||
0,
|
||||
0,
|
||||
"Show this node's geometry output in the spreadsheet in Node mode");
|
||||
"Show this node's geometry output in the spreadsheet");
|
||||
UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_active_preview_toggle");
|
||||
UI_block_emboss_set(node->block, UI_EMBOSS);
|
||||
}
|
||||
|
|
|
@ -1694,55 +1694,6 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static void disable_active_preview_on_all_nodes(bNodeTree *ntree)
|
||||
{
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
node->flag &= ~NODE_ACTIVE_PREVIEW;
|
||||
}
|
||||
}
|
||||
|
||||
static int node_active_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
disable_active_preview_on_all_nodes(ntree);
|
||||
bNode *active_node = nodeGetActive(ntree);
|
||||
active_node->flag |= NODE_ACTIVE_PREVIEW;
|
||||
|
||||
/* Tag for update, so that dependent objects are reevaluated. This is necessary when a
|
||||
* spreadsheet editor displays data from a node. */
|
||||
LISTBASE_FOREACH (wmWindow *, window, &((wmWindowManager *)bmain->wm.first)->windows) {
|
||||
bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
if (area->spacetype == SPACE_SPREADSHEET) {
|
||||
SpaceSpreadsheet *sspreadsheet = area->spacedata.first;
|
||||
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_NODE) {
|
||||
DEG_id_tag_update(&ntree->id, ID_RECALC_COPY_ON_WRITE);
|
||||
ED_area_tag_redraw(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void NODE_OT_active_preview_toggle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Toggle Active Preview";
|
||||
ot->description = "Toggle active preview state of node";
|
||||
ot->idname = "NODE_OT_active_preview_toggle";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = node_active_preview_toggle_exec;
|
||||
ot->poll = ED_operator_node_active;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ****************** Mute operator *********************** */
|
||||
|
||||
static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
|
@ -2241,13 +2192,25 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|||
/* make sure all clipboard nodes would be valid in the target tree */
|
||||
bool all_nodes_valid = true;
|
||||
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
|
||||
if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) {
|
||||
const char *disabled_hint = NULL;
|
||||
if (!node->typeinfo->poll_instance ||
|
||||
!node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
|
||||
all_nodes_valid = false;
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s",
|
||||
node->name,
|
||||
ntree->id.name + 2);
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s:\n %s",
|
||||
node->name,
|
||||
ntree->id.name + 2,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Cannot add node %s into node tree %s",
|
||||
node->name,
|
||||
ntree->id.name + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!all_nodes_valid) {
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
#include "BKE_node_ui_storage.hh"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
|
@ -37,44 +42,77 @@
|
|||
|
||||
using blender::IndexRange;
|
||||
using blender::Map;
|
||||
using blender::MultiValueMap;
|
||||
using blender::Set;
|
||||
using blender::StringRef;
|
||||
|
||||
struct AttributeSearchData {
|
||||
const bNodeTree &node_tree;
|
||||
const bNode &node;
|
||||
|
||||
uiBut *search_button;
|
||||
|
||||
/* Used to keep track of a button pointer over multiple redraws. Since the UI code
|
||||
* may reallocate the button, without this we might end up with a dangling pointer. */
|
||||
uiButStore *button_store;
|
||||
uiBlock *button_store_block;
|
||||
bNodeSocket &socket;
|
||||
};
|
||||
|
||||
/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */
|
||||
BLI_STATIC_ASSERT(std::is_trivially_destructible_v<AttributeSearchData>, "");
|
||||
|
||||
static StringRef attribute_data_type_string(const CustomDataType type)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name);
|
||||
return StringRef(IFACE_(name));
|
||||
}
|
||||
|
||||
static StringRef attribute_domain_string(const AttributeDomain domain)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name);
|
||||
return StringRef(IFACE_(name));
|
||||
}
|
||||
|
||||
/* Unicode arrow. */
|
||||
#define MENU_SEP "\xe2\x96\xb6"
|
||||
|
||||
static bool attribute_search_item_add(uiSearchItems *items, const AvailableAttributeInfo &item)
|
||||
{
|
||||
const StringRef data_type_name = attribute_data_type_string(item.data_type);
|
||||
const StringRef domain_name = attribute_domain_string(item.domain);
|
||||
std::string search_item_text = domain_name + " " + MENU_SEP + item.name + UI_SEP_CHAR +
|
||||
data_type_name;
|
||||
|
||||
return UI_search_item_add(
|
||||
items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0);
|
||||
}
|
||||
|
||||
static void attribute_search_update_fn(
|
||||
const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
|
||||
{
|
||||
AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
|
||||
NodeTreeUIStorage *tree_ui_storage = data->node_tree.ui_storage;
|
||||
if (tree_ui_storage == nullptr) {
|
||||
return;
|
||||
}
|
||||
const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context(
|
||||
C, data->node_tree, data->node);
|
||||
if (ui_storage == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const MultiValueMap<std::string, AvailableAttributeInfo> &attribute_hints =
|
||||
ui_storage->attribute_hints;
|
||||
const Set<AvailableAttributeInfo> &attribute_hints = ui_storage->attribute_hints;
|
||||
|
||||
if (str[0] != '\0' && attribute_hints.lookup_as(StringRef(str)).is_empty()) {
|
||||
/* Any string may be valid, so add the current search string with the hints. */
|
||||
UI_search_item_add(items, str, (void *)str, ICON_ADD, 0, 0);
|
||||
/* Any string may be valid, so add the current search string along with the hints. */
|
||||
if (str[0] != '\0') {
|
||||
/* Note that the attribute domain and data type are dummies, since
|
||||
* #AvailableAttributeInfo equality is only based on the string. */
|
||||
if (!attribute_hints.contains(AvailableAttributeInfo{str, ATTR_DOMAIN_AUTO, CD_PROP_BOOL})) {
|
||||
tree_ui_storage->dummy_info_for_search.name = std::string(str);
|
||||
UI_search_item_add(items, str, &tree_ui_storage->dummy_info_for_search, ICON_ADD, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (str[0] == '\0' && !is_first) {
|
||||
/* Allow clearing the text field when the string is empty, but not on the first pass,
|
||||
* or opening an attribute field for the first time would show this search item. */
|
||||
UI_search_item_add(items, str, (void *)str, ICON_X, 0, 0);
|
||||
tree_ui_storage->dummy_info_for_search.name = std::string(str);
|
||||
UI_search_item_add(items, str, &tree_ui_storage->dummy_info_for_search, ICON_X, 0, 0);
|
||||
}
|
||||
|
||||
/* Don't filter when the menu is first opened, but still run the search
|
||||
|
@ -82,16 +120,16 @@ static void attribute_search_update_fn(
|
|||
const char *string = is_first ? "" : str;
|
||||
|
||||
StringSearch *search = BLI_string_search_new();
|
||||
for (const std::string &attribute_name : attribute_hints.keys()) {
|
||||
BLI_string_search_add(search, attribute_name.c_str(), (void *)&attribute_name);
|
||||
for (const AvailableAttributeInfo &item : attribute_hints) {
|
||||
BLI_string_search_add(search, item.name.c_str(), (void *)&item);
|
||||
}
|
||||
|
||||
std::string **filtered_items;
|
||||
AvailableAttributeInfo **filtered_items;
|
||||
const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
|
||||
|
||||
for (const int i : IndexRange(filtered_amount)) {
|
||||
std::string *item = filtered_items[i];
|
||||
if (!UI_search_item_add(items, item->c_str(), item, ICON_NONE, 0, 0)) {
|
||||
const AvailableAttributeInfo *item = filtered_items[i];
|
||||
if (!attribute_search_item_add(items, *item)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -100,12 +138,14 @@ static void attribute_search_update_fn(
|
|||
BLI_string_search_free(search);
|
||||
}
|
||||
|
||||
static void attribute_search_free_fn(void *arg)
|
||||
static void attribute_search_exec_fn(bContext *UNUSED(C), void *data_v, void *item_v)
|
||||
{
|
||||
AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
|
||||
AttributeSearchData *data = static_cast<AttributeSearchData *>(data_v);
|
||||
AvailableAttributeInfo *item = static_cast<AvailableAttributeInfo *>(item_v);
|
||||
|
||||
UI_butstore_free(data->button_store_block, data->button_store);
|
||||
delete data;
|
||||
bNodeSocket &socket = data->socket;
|
||||
bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
|
||||
BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
|
||||
}
|
||||
|
||||
void node_geometry_add_attribute_search_button(const bNodeTree *node_tree,
|
||||
|
@ -132,22 +172,17 @@ void node_geometry_add_attribute_search_button(const bNodeTree *node_tree,
|
|||
0.0f,
|
||||
"");
|
||||
|
||||
AttributeSearchData *data = new AttributeSearchData{
|
||||
*node_tree,
|
||||
*node,
|
||||
but,
|
||||
UI_butstore_create(block),
|
||||
block,
|
||||
};
|
||||
|
||||
UI_butstore_register(data->button_store, &data->search_button);
|
||||
AttributeSearchData *data = OBJECT_GUARDED_NEW(
|
||||
AttributeSearchData, {*node_tree, *node, *static_cast<bNodeSocket *>(socket_ptr->data)});
|
||||
|
||||
UI_but_func_search_set_results_are_suggestions(but, true);
|
||||
UI_but_func_search_set_sep_string(but, MENU_SEP);
|
||||
UI_but_func_search_set(but,
|
||||
nullptr,
|
||||
attribute_search_update_fn,
|
||||
static_cast<void *>(data),
|
||||
attribute_search_free_fn,
|
||||
true,
|
||||
nullptr,
|
||||
attribute_search_exec_fn,
|
||||
nullptr);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue