diff options
author | Julian Eisel <julian@blender.org> | 2022-05-27 12:33:26 +0300 |
---|---|---|
committer | Julian Eisel <julian@blender.org> | 2022-05-27 12:33:26 +0300 |
commit | e1ced645fa208b3b77e07c99cb289cf7fa659ad3 (patch) | |
tree | 60f5d3478007bb1fbb5c09425c32f256dfab74d6 | |
parent | 3f7015a79f09783bc560dce109fbd3eed6f6aa2a (diff) | |
parent | 5162632a209176350cc951a1783b3b010c910acd (diff) |
Merge branch 'asset-browser-grid-view' into file-browser-grid-view
423 files changed, 8513 insertions, 5071 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 85827e01af5..3e97e393f17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1756,6 +1756,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC") "/wd4828" # The file contains a character that is illegal "/wd4996" # identifier was declared deprecated "/wd4661" # no suitable definition provided for explicit template instantiation request + "/wd4848" # 'no_unique_address' is a vendor extension in C++17 # errors: "/we4013" # 'function' undefined; assuming extern returning int "/we4133" # incompatible pointer types diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 0a20bd83c22..b29251b67cc 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -2,8 +2,7 @@ # <pep8 compliant> -SCRIPT_HELP_MSG = """ - +""" API dump in RST files --------------------- Run this script from Blender's root path once you have compiled Blender @@ -14,10 +13,10 @@ API dump in RST files providing ./blender is or links to the blender executable To choose sphinx-in directory: - blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --output ../python_api + blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --output=../python_api For quick builds: - blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --partial bmesh.* + blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --partial=bmesh.* Sphinx: HTML generation @@ -36,18 +35,17 @@ Sphinx: PDF generation sphinx-build -b latex doc/python_api/sphinx-in doc/python_api/sphinx-out cd doc/python_api/sphinx-out make - """ try: - import bpy # Blender module + import bpy # Blender module. except ImportError: print("\nERROR: this script must run from inside Blender") - print(SCRIPT_HELP_MSG) + print(__doc__) import sys sys.exit() -import rna_info # Blender module +import rna_info # Blender module. def rna_info_BuildRNAInfo_cache(): @@ -69,15 +67,12 @@ import warnings from textwrap import indent -from platform import platform -PLATFORM = platform().split('-')[0].lower() # 'linux', 'darwin', 'windows' - SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) -# For now, ignore add-ons and internal subclasses of 'bpy.types.PropertyGroup'. +# For now, ignore add-ons and internal sub-classes of `bpy.types.PropertyGroup`. # # Besides disabling this line, the main change will be to add a -# 'toctree' to 'write_rst_index' which contains the generated rst files. +# 'toctree' to 'write_rst_index' which contains the generated RST files. # This 'toctree' can be generated automatically. # # See: D6261 for reference. @@ -85,92 +80,110 @@ USE_ONLY_BUILTIN_RNA_TYPES = True def handle_args(): - ''' + """ Parse the args passed to Blender after "--", ignored by Blender - ''' + """ import argparse # When --help is given, print the usage text parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, - usage=SCRIPT_HELP_MSG + usage=__doc__ ) # optional arguments - parser.add_argument("-p", "--partial", - dest="partial", - type=str, - default="", - help="Use a wildcard to only build specific module(s)\n" - "Example: --partial bmesh*\n", - required=False) - - parser.add_argument("-f", "--fullrebuild", - dest="full_rebuild", - default=False, - action='store_true', - help="Rewrite all rst files in sphinx-in/ " - "(default=False)", - required=False) - - parser.add_argument("-b", "--bpy", - dest="bpy", - default=False, - action='store_true', - help="Write the rst file of the bpy module " - "(default=False)", - required=False) - - parser.add_argument("-o", "--output", - dest="output_dir", - type=str, - default=SCRIPT_DIR, - help="Path of the API docs (default=<script dir>)", - required=False) - - parser.add_argument("-B", "--sphinx-build", - dest="sphinx_build", - default=False, - action='store_true', - help="Build the html docs by running:\n" - "sphinx-build SPHINX_IN SPHINX_OUT\n" - "(default=False; does not depend on -P)", - required=False) - - parser.add_argument("-P", "--sphinx-build-pdf", - dest="sphinx_build_pdf", - default=False, - action='store_true', - help="Build the pdf by running:\n" - "sphinx-build -b latex SPHINX_IN SPHINX_OUT_PDF\n" - "(default=False; does not depend on -B)", - required=False) - - parser.add_argument("-R", "--pack-reference", - dest="pack_reference", - default=False, - action='store_true', - help="Pack all necessary files in the deployed dir.\n" - "(default=False; use with -B and -P)", - required=False) - - parser.add_argument("-l", "--log", - dest="log", - default=False, - action='store_true', - help="Log the output of the API dump and sphinx|latex " - "warnings and errors (default=False).\n" - "If given, save logs in:\n" - "* OUTPUT_DIR/.bpy.log\n" - "* OUTPUT_DIR/.sphinx-build.log\n" - "* OUTPUT_DIR/.sphinx-build_pdf.log\n" - "* OUTPUT_DIR/.latex_make.log", - required=False) - - # parse only the args passed after '--' + parser.add_argument( + "-p", "--partial", + dest="partial", + type=str, + default="", + help="Use a wildcard to only build specific module(s)\n" + "Example: --partial\"=bmesh*\"\n", + required=False, + ) + + parser.add_argument( + "-f", "--fullrebuild", + dest="full_rebuild", + default=False, + action='store_true', + help="Rewrite all RST files in sphinx-in/ " + "(default=False)", + required=False, + ) + + parser.add_argument( + "-b", "--bpy", + dest="bpy", + default=False, + action='store_true', + help="Write the RST file of the bpy module " + "(default=False)", + required=False, + ) + + parser.add_argument( + "-o", "--output", + dest="output_dir", + type=str, + default=SCRIPT_DIR, + help="Path of the API docs (default=<script dir>)", + required=False, + ) + + parser.add_argument( + "-B", "--sphinx-build", + dest="sphinx_build", + default=False, + action='store_true', + help="Build the html docs by running:\n" + "sphinx-build SPHINX_IN SPHINX_OUT\n" + "(default=False; does not depend on -P)", + required=False, + ) + + parser.add_argument( + "-P", "--sphinx-build-pdf", + dest="sphinx_build_pdf", + default=False, + action='store_true', + help="Build the pdf by running:\n" + "sphinx-build -b latex SPHINX_IN SPHINX_OUT_PDF\n" + "(default=False; does not depend on -B)", + required=False, + ) + + parser.add_argument( + "-R", "--pack-reference", + dest="pack_reference", + default=False, + action='store_true', + help="Pack all necessary files in the deployed dir.\n" + "(default=False; use with -B and -P)", + required=False, + ) + + parser.add_argument( + "-l", "--log", + dest="log", + default=False, + action='store_true', + help=( + "Log the output of the API dump and sphinx|latex " + "warnings and errors (default=False).\n" + "If given, save logs in:\n" + "* OUTPUT_DIR/.bpy.log\n" + "* OUTPUT_DIR/.sphinx-build.log\n" + "* OUTPUT_DIR/.sphinx-build_pdf.log\n" + "* OUTPUT_DIR/.latex_make.log" + ), + required=False, + ) + + # Parse only the arguments passed after "--". argv = [] if "--" in sys.argv: - argv = sys.argv[sys.argv.index("--") + 1:] # get all args after "--" + argv = sys.argv[sys.argv.index("--") + 1:] # Get all arguments after "--". return parser.parse_args(argv) @@ -179,7 +192,7 @@ ARGS = handle_args() # ----------------------------------BPY----------------------------------------- -BPY_LOGGER = logging.getLogger('bpy') +BPY_LOGGER = logging.getLogger("bpy") BPY_LOGGER.setLevel(logging.DEBUG) """ @@ -193,7 +206,7 @@ or ./blender -b -noaudio --factory-startup -P doc/python_api/sphinx_doc_gen.py -- -f -B """ -# Switch for quick testing so doc-builds don't take so long +# Switch for quick testing so doc-builds don't take so long. if not ARGS.partial: # full build FILTER_BPY_OPS = None @@ -261,7 +274,7 @@ else: # ------ # Filter # - # TODO, support bpy.ops and bpy.types filtering + # TODO: support `bpy.ops` and `bpy.types` filtering. import fnmatch m = None EXCLUDE_MODULES = [m for m in EXCLUDE_MODULES if not fnmatch.fnmatchcase(m, ARGS.partial)] @@ -275,7 +288,7 @@ else: if FILTER_BPY_TYPES: EXCLUDE_MODULES.remove("bpy.types") - print(FILTER_BPY_TYPES) + # print(FILTER_BPY_TYPES) EXCLUDE_INFO_DOCS = (not fnmatch.fnmatchcase("info", ARGS.partial)) @@ -283,7 +296,7 @@ else: del fnmatch BPY_LOGGER.debug( - "Partial Doc Build, Skipping: %s\n" % + "Partial Doc Build, Skipping: %s\n", "\n ".join(sorted(EXCLUDE_MODULES))) # @@ -293,13 +306,13 @@ else: try: __import__("aud") except ImportError: - BPY_LOGGER.debug("Warning: Built without 'aud' module, docs incomplete...") + BPY_LOGGER.debug("Warning: Built without \"aud\" module, docs incomplete...") EXCLUDE_MODULES.append("aud") try: __import__("freestyle") except ImportError: - BPY_LOGGER.debug("Warning: Built without 'freestyle' module, docs incomplete...") + BPY_LOGGER.debug("Warning: Built without \"freestyle\" module, docs incomplete...") EXCLUDE_MODULES.extend([ "freestyle", "freestyle.chainingiterators", @@ -329,13 +342,10 @@ EXTRA_SOURCE_FILES = ( # examples EXAMPLES_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "examples")) -EXAMPLE_SET = set() -for f in os.listdir(EXAMPLES_DIR): - if f.endswith(".py"): - EXAMPLE_SET.add(os.path.splitext(f)[0]) +EXAMPLE_SET = set(os.path.splitext(f)[0] for f in os.listdir(EXAMPLES_DIR) if f.endswith(".py")) EXAMPLE_SET_USED = set() -# rst files dir +# RST files directory. RST_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "rst")) # extra info, not api reference docs @@ -362,35 +372,52 @@ RNA_BLACKLIST = { "PreferencesSystem": {"language", } } +# Support suppressing errors when attributes collide with methods, +# use `noindex` on the attributes / data declarations. +# +# NOTE: in general this should be avoided but changing it would break the API, +# so explicitly suppress warnings instead. +# +# NOTE: Currently some API generation doesn't support this is it is not used yet, +# see references to `RST_NOINDEX_ATTR` in code comments. +# +# A set of tuple identifiers: `(module, type, attr)`. +RST_NOINDEX_ATTR = { + # Render is both a method and an attribute, from looking into this + # having both doesn't cause problems in practice since the `render` method + # is registered and called from C code where the attribute is accessed from the instance. + ("bpy.types", "RenderEngine", "render"), +} + MODULE_GROUPING = { "bmesh.types": ( - ("Base Mesh Type", '-'), + ("Base Mesh Type", "-"), "BMesh", - ("Mesh Elements", '-'), + ("Mesh Elements", "-"), "BMVert", "BMEdge", "BMFace", "BMLoop", - ("Sequence Accessors", '-'), + ("Sequence Accessors", "-"), "BMElemSeq", "BMVertSeq", "BMEdgeSeq", "BMFaceSeq", "BMLoopSeq", "BMIter", - ("Selection History", '-'), + ("Selection History", "-"), "BMEditSelSeq", "BMEditSelIter", - ("Custom-Data Layer Access", '-'), + ("Custom-Data Layer Access", "-"), "BMLayerAccessVert", "BMLayerAccessEdge", "BMLayerAccessFace", "BMLayerAccessLoop", "BMLayerCollection", "BMLayerItem", - ("Custom-Data Layer Types", '-'), + ("Custom-Data Layer Types", "-"), "BMLoopUV", - "BMDeformVert" + "BMDeformVert", ) } @@ -419,7 +446,7 @@ else: BLENDER_VERSION_HASH_HTML_LINK = BLENDER_VERSION_HASH BLENDER_VERSION_DATE = time.strftime("%Y-%m-%d") -# '2_83' +# Example: `2_83`. BLENDER_VERSION_PATH = "%d_%d" % (bpy.app.version[0], bpy.app.version[1]) # --------------------------DOWNLOADABLE FILES---------------------------------- @@ -470,8 +497,8 @@ if ARGS.sphinx_build_pdf: # --------------------------------API DUMP-------------------------------------- -# lame, python won't give some access -ClassMethodDescriptorType = type(dict.__dict__['fromkeys']) +# Lame, python won't give some access. +ClassMethodDescriptorType = type(dict.__dict__["fromkeys"]) MethodDescriptorType = type(dict.get) GetSetDescriptorType = type(int.real) StaticMethodType = type(staticmethod(lambda: None)) @@ -502,8 +529,15 @@ def import_value_from_module(module_name, import_name): return ns["value"] +def execfile(filepath): + global_namespace = {"__file__": filepath, "__name__": "__main__"} + with open(filepath) as file_handle: + exec(compile(file_handle.read(), filepath, 'exec'), global_namespace) + + def escape_rst(text): - """ Escape plain text which may contain characters used by RST. + """ + Escape plain text which may contain characters used by RST. """ return text.translate(escape_rst.trans) @@ -517,36 +551,41 @@ escape_rst.trans = str.maketrans({ def is_struct_seq(value): - return isinstance(value, tuple) and type(tuple) != tuple and hasattr(value, "n_fields") + return isinstance(value, tuple) and type(value) != tuple and hasattr(value, "n_fields") def undocumented_message(module_name, type_name, identifier): + BPY_LOGGER.debug( + "Undocumented: module %s, type: %s, id: %s is not documented", + module_name, type_name, identifier, + ) + return "Undocumented, consider `contributing <https://developer.blender.org/T51061>`__." def range_str(val): - ''' + """ Converts values to strings for the range directive. (unused function it seems) - ''' + """ if val < -10000000: - return '-inf' + return "-inf" elif val > 10000000: - return 'inf' + return "inf" elif type(val) == float: - return '%g' % val + return "%g" % val else: return str(val) def example_extract_docstring(filepath): - ''' + """ Return (text, line_no, line_no_has_content) where: - ``text`` is the doc-string text. - ``line_no`` is the line the doc-string text ends. - ``line_no_has_content`` when False, this file only contains a doc-string. There is no need to include the remainder. - ''' + """ file = open(filepath, "r", encoding="utf-8") line = file.readline() line_no = 0 @@ -561,8 +600,7 @@ def example_extract_docstring(filepath): line_no += 1 if line.startswith('"""'): break - else: - text.append(line.rstrip()) + text.append(line.rstrip()) line_no += 1 line_no_has_content = False @@ -590,7 +628,7 @@ def title_string(text, heading_char, double=False): def write_example_ref(ident, fw, example_id, ext="py"): if example_id in EXAMPLE_SET: - # extract the comment + # Extract the comment. filepath = os.path.join("..", "examples", "%s.%s" % (example_id, ext)) filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath) @@ -613,9 +651,9 @@ def write_example_ref(ident, fw, example_id, ext="py"): EXAMPLE_SET_USED.add(example_id) else: if bpy.app.debug: - BPY_LOGGER.debug("\tskipping example: " + example_id) + BPY_LOGGER.debug("\tskipping example: %s", example_id) - # Support for numbered files bpy.types.Operator -> bpy.types.Operator.1.py + # Support for numbered files `bpy.types.Operator` -> `bpy.types.Operator.1.py`. i = 1 while True: example_id_num = "%s.%d" % (example_id, i) @@ -627,22 +665,22 @@ def write_example_ref(ident, fw, example_id, ext="py"): def write_indented_lines(ident, fn, text, strip=True): - ''' - Apply same indentation to all lines in a multilines text. - ''' + """ + Apply same indentation to all lines in a multi-lines text. + """ if text is None: return lines = text.split("\n") - # strip empty lines from the start/end + # Strip empty lines from the start/end. while lines and not lines[0].strip(): del lines[0] while lines and not lines[-1].strip(): del lines[-1] if strip: - # set indentation to <indent> + # Set indentation to `<indent>`. ident_strip = 1000 for l in lines: if l.strip(): @@ -650,15 +688,15 @@ def write_indented_lines(ident, fn, text, strip=True): for l in lines: fn(ident + l[ident_strip:] + "\n") else: - # add <indent> number of blanks to the current indentation + # Add <indent> number of blanks to the current indentation. for l in lines: fn(ident + l + "\n") def pymethod2sphinx(ident, fw, identifier, py_func): - ''' + """ class method to sphinx - ''' + """ arg_str = inspect.formatargspec(*inspect.getargspec(py_func)) if arg_str.startswith("(self, "): arg_str = "(" + arg_str[7:] @@ -676,9 +714,9 @@ def pymethod2sphinx(ident, fw, identifier, py_func): def pyfunc2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True): - ''' + """ function or class method to sphinx - ''' + """ if type(py_func) == MethodType: return @@ -688,7 +726,7 @@ def pyfunc2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_cla if not is_class: func_type = "function" - # the rest are class methods + # The rest are class methods. elif arg_str.startswith("(self, ") or arg_str == "(self)": arg_str = "()" if (arg_str == "(self)") else ("(" + arg_str[7:]) func_type = "method" @@ -726,10 +764,12 @@ def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier): if type(descr) == GetSetDescriptorType: fw(ident + ".. attribute:: %s\n\n" % identifier) + # NOTE: `RST_NOINDEX_ATTR` currently not supported (as it's not used). write_indented_lines(ident + " ", fw, doc, False) fw("\n") - elif type(descr) == MemberDescriptorType: # same as above but use 'data' + elif type(descr) == MemberDescriptorType: # same as above but use "data" fw(ident + ".. data:: %s\n\n" % identifier) + # NOTE: `RST_NOINDEX_ATTR` currently not supported (as it's not used). write_indented_lines(ident + " ", fw, doc, False) fw("\n") elif type(descr) in {MethodDescriptorType, ClassMethodDescriptorType}: @@ -743,11 +783,11 @@ def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier): def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True): - ''' - c defined function to sphinx. - ''' + """ + C defined function to sphinx. + """ - # dump the docstring, assume its formatted correctly + # Dump the doc-string, assume its formatted correctly. if py_func.__doc__: write_indented_lines(ident, fw, py_func.__doc__, False) fw("\n") @@ -764,14 +804,16 @@ def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_ def pyprop2sphinx(ident, fw, identifier, py_prop): - ''' + """ Python property to sphinx - ''' - # readonly properties use "data" directive, variables use "attribute" directive + """ + # Read-only properties use "data" directive, variables use "attribute" directive. if py_prop.fset is None: fw(ident + ".. data:: %s\n\n" % identifier) else: fw(ident + ".. attribute:: %s\n\n" % identifier) + + # NOTE: `RST_NOINDEX_ATTR` currently not supported (as it's not used). write_indented_lines(ident + " ", fw, py_prop.__doc__) fw("\n") if py_prop.fset is None: @@ -789,8 +831,8 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): if module_all: module_dir = module_all - # TODO - currently only used for classes - # grouping support + # TODO: currently only used for classes. + # Grouping support. module_grouping = MODULE_GROUPING.get(module_name) def module_grouping_index(name): @@ -810,7 +852,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): def module_grouping_sort_key(name): return module_grouping_index(name) - # done grouping support + # Done grouping support. file = open(filepath, "w", encoding="utf-8") @@ -825,8 +867,8 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): fw(module.__doc__.strip()) fw("\n\n") - # write submodules - # we could also scan files but this ensures __all__ is used correctly + # Write sub-modules. + # We could also scan files but this ensures `__all__` is used correctly. if module_all or module_all_extra: submod_name = None submod = None @@ -859,7 +901,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): pymodule2sphinx(basepath, submod_name_full, submod, "%s submodule" % module_name, ()) fw("\n") del submod_ls - # done writing submodules! + # Done writing sub-modules! write_example_ref("", fw, module_name) @@ -870,10 +912,10 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): continue if key in module_all_extra: continue - # naughty, we also add getset's into PyStructs, this is not typical py but also not incorrect. + # Naughty! We also add `getset` to `PyStruct`, this is not typical Python but also not incorrect. - # type_name is only used for examples and messages - # "<class 'bpy.app.handlers'>" --> bpy.app.handlers + # `type_name` is only used for examples and messages: + # `<class 'bpy.app.handlers'>` -> `bpy.app.handlers`. type_name = str(type(module)).strip("<>").split(" ", 1)[-1][1:-1] if type(descr) == types.GetSetDescriptorType: py_descr2sphinx("", fw, descr, module_name, type_name, key) @@ -889,13 +931,13 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): value_type = type(value) descr_sorted.append((key, descr, value, type(value))) - # sort by the valye type + # Sort by the value type. descr_sorted.sort(key=lambda descr_data: str(descr_data[3])) for key, descr, value, value_type in descr_sorted: if key in module_all_extra: continue - # must be documented as a submodule + # Must be documented as a sub-module. if is_struct_seq(value): continue @@ -909,7 +951,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): classes = [] submodules = [] - # use this list so we can sort by type + # Use this list so we can sort by type. module_dir_value_type = [] for attribute in module_dir: @@ -919,7 +961,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): if attribute in attribute_set: continue - if attribute.startswith("n_"): # annoying exception, needed for bpy.app + if attribute.startswith("n_"): # Annoying exception, needed for `bpy.app`. continue # workaround for bpy.app documenting .index() and .count() @@ -940,31 +982,31 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): if value_type == FunctionType: pyfunc2sphinx("", fw, module_name, None, attribute, value, is_class=False) - # both the same at the moment but to be future proof + # Both the same at the moment but to be future proof. elif value_type in {types.BuiltinMethodType, types.BuiltinFunctionType}: - # note: can't get args from these, so dump the string as is - # this means any module used like this must have fully formatted docstrings. + # NOTE: can't get args from these, so dump the string as is + # this means any module used like this must have fully formatted doc-strings. py_c_func2sphinx("", fw, module_name, None, attribute, value, is_class=False) elif value_type == type: classes.append((attribute, value)) elif issubclass(value_type, types.ModuleType): submodules.append((attribute, value)) elif issubclass(value_type, (bool, int, float, str, tuple)): - # constant, not much fun we can do here except to list it. - # TODO, figure out some way to document these! + # Constant, not much fun we can do here except to list it. + # TODO: figure out some way to document these! fw(".. data:: %s\n\n" % attribute) write_indented_lines(" ", fw, "Constant value %s" % repr(value), False) fw("\n") else: - BPY_LOGGER.debug("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__)) + BPY_LOGGER.debug("\tnot documenting %s.%s of %r type", module_name, attribute, value_type.__name__) continue attribute_set.add(attribute) - # TODO, more types... + # TODO: more types. del module_dir_value_type - # TODO, bpy_extras does this already, mathutils not. - ''' + # TODO: `bpy_extras` does this already, `mathutils` not. + """ if submodules: fw("\n" "**********\n" @@ -975,12 +1017,12 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): for attribute, submod in submodules: fw("* :mod:`%s.%s`\n" % (module_name, attribute)) fw("\n") - ''' + """ if module_grouping is not None: classes.sort(key=lambda pair: module_grouping_sort_key(pair[0])) - # write collected classes now + # Write collected classes now. for (type_name, value) in classes: if module_grouping is not None: @@ -988,7 +1030,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): if heading: fw(title_string(heading, heading_char)) - # May need to be its own function + # May need to be its own function. if value.__doc__: if value.__doc__.startswith(".. class::"): fw(value.__doc__) @@ -1007,7 +1049,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): if type(descr) == ClassMethodDescriptorType: py_descr2sphinx(" ", fw, descr, module_name, type_name, key) - # needed for pure Python classes + # Needed for pure Python classes. for key, descr in descr_items: if type(descr) == FunctionType: pyfunc2sphinx(" ", fw, module_name, type_name, key, descr, is_class=True) @@ -1031,7 +1073,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): file.close() -# Changes in Blender will force errors here +# Changes In Blender will force errors here. context_type_map = { # context_member: (RNA type, is_collection) "active_annotation_layer": ("GPencilLayer", False), @@ -1106,7 +1148,6 @@ context_type_map = { "selected_editable_keyframes": ("Keyframe", True), "selected_editable_objects": ("Object", True), "selected_editable_sequences": ("Sequence", True), - "selected_ids": ("ID", True), "selected_files": ("FileSelectEntry", True), "selected_ids": ("ID", True), "selected_nla_strips": ("NlaStrip", True), @@ -1140,7 +1181,7 @@ context_type_map = { def pycontext2sphinx(basepath): - # Only use once. very irregular + # Only use once. very irregular. filepath = os.path.join(basepath, "bpy.context.rst") file = open(filepath, "w", encoding="utf-8") @@ -1161,8 +1202,8 @@ def pycontext2sphinx(basepath): fw(title_string("Global Context", "-")) fw("These properties are available in any contexts.\n\n") - # very silly. could make these global and only access once. - # structs, funcs, ops, props = rna_info.BuildRNAInfo() + # Very silly. could make these global and only access once: + # `structs, funcs, ops, props = rna_info.BuildRNAInfo()`. structs, funcs, ops, props = rna_info_BuildRNAInfo_cache() struct = structs[("", "Context")] struct_blacklist = RNA_BLACKLIST.get(struct.identifier, ()) @@ -1171,7 +1212,7 @@ def pycontext2sphinx(basepath): sorted_struct_properties = struct.properties[:] sorted_struct_properties.sort(key=lambda prop: prop.identifier) - # First write RNA + # First write RNA. for prop in sorted_struct_properties: # Support blacklisting props. if prop.identifier in struct_blacklist: @@ -1185,14 +1226,14 @@ def pycontext2sphinx(basepath): if prop.description: fw(" %s\n\n" % prop.description) - # special exception, can't use generic code here for enums + # Special exception, can't use generic code here for enums. if prop.type == "enum": enum_text = pyrna_enum2sphinx(prop) if enum_text: write_indented_lines(" ", fw, enum_text) fw("\n") del enum_text - # end enum exception + # End enum exception. fw(" :type: %s\n\n" % type_descr) @@ -1209,7 +1250,7 @@ def pycontext2sphinx(basepath): unique_context_strings = set() for ctx_str, ctx_members in sorted(context_member_map.items()): subsection = "%s Context" % ctx_str.split("_")[0].title() - fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * '-'))) + fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * "-"))) for member in ctx_members: unique_all_len = len(unique) unique.add(member) @@ -1231,7 +1272,7 @@ def pycontext2sphinx(basepath): (member, __file__)) from None fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type)) - # generate typemap... + # Generate type-map: # for member in sorted(unique_context_strings): # print(' "%s": ("", False),' % member) if len(context_type_map) > len(unique_context_strings): @@ -1239,13 +1280,14 @@ def pycontext2sphinx(basepath): "Some types are not used: %s" % str([member for member in context_type_map if member not in unique_context_strings])) else: - pass # will have raised an error above + pass # Will have raised an error above. file.close() def pyrna_enum2sphinx(prop, use_empty_descriptions=False): - """ write a bullet point list of enum + descriptions + """ + Write a bullet point list of enum + descriptions. """ if use_empty_descriptions: @@ -1272,13 +1314,14 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False): def pyrna2sphinx(basepath): - """ bpy.types and bpy.ops """ - # structs, funcs, ops, props = rna_info.BuildRNAInfo() - structs, funcs, ops, props = rna_info_BuildRNAInfo_cache() + ``bpy.types`` and ``bpy.ops``. + """ + # `structs, funcs, ops, props = rna_info.BuildRNAInfo()` + structs, _funcs, ops, _props = rna_info_BuildRNAInfo_cache() if USE_ONLY_BUILTIN_RNA_TYPES: - # Ignore properties that use non 'bpy.types' properties. + # Ignore properties that use non `bpy.types` properties. structs_blacklist = { v.identifier for v in structs.values() if v.module_name != "bpy.types" @@ -1335,7 +1378,7 @@ def pyrna2sphinx(basepath): if prop.name or prop.description: fw(indent(", ".join(val for val in (prop.name, prop.description) if val), ident + " ") + "\n\n") - # special exception, can't use generic code here for enums + # Special exception, can't use generic code here for enums. if enum_text: write_indented_lines(ident + " ", fw, enum_text) fw("\n") @@ -1415,41 +1458,47 @@ def pyrna2sphinx(basepath): fw(" %s\n\n" % struct.description) - # properties sorted in alphabetical order + # Properties sorted in alphabetical order. sorted_struct_properties = struct.properties[:] sorted_struct_properties.sort(key=lambda prop: prop.identifier) - # support blacklisting props + # Support blacklisting props. struct_blacklist = RNA_BLACKLIST.get(struct_id, ()) for prop in sorted_struct_properties: + identifier = prop.identifier - # support blacklisting props - if prop.identifier in struct_blacklist: + # Support blacklisting props. + if identifier in struct_blacklist: continue type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID) - # readonly properties use "data" directive, variables properties use "attribute" directive - if 'readonly' in type_descr: - fw(" .. data:: %s\n\n" % prop.identifier) + # Read-only properties use "data" directive, variables properties use "attribute" directive. + if "readonly" in type_descr: + fw(" .. data:: %s\n" % identifier) else: - fw(" .. attribute:: %s\n\n" % prop.identifier) + fw(" .. attribute:: %s\n" % identifier) + # Also write `noindex` on requerst. + if ("bpy.types", struct_id, identifier) in RST_NOINDEX_ATTR: + fw(" :noindex:\n") + fw("\n") + if prop.description: write_indented_lines(" ", fw, prop.description, False) fw("\n") - # special exception, can't use generic code here for enums + # Special exception, can't use generic code here for enums. if prop.type == "enum": enum_text = pyrna_enum2sphinx(prop) if enum_text: write_indented_lines(" ", fw, enum_text) fw("\n") del enum_text - # end enum exception + # End enum exception. fw(" :type: %s\n\n" % type_descr) - # Python attributes + # Python attributes. py_properties = struct.get_py_properties() py_prop = None for identifier, py_prop in py_properties: @@ -1474,17 +1523,17 @@ def pyrna2sphinx(basepath): if len(func.return_values) == 1: write_param(" ", fw, func.return_values[0], is_return=True) - elif func.return_values: # multiple return values + elif func.return_values: # Multiple return values. fw(" :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values)) for prop in func.return_values: - # TODO, pyrna_enum2sphinx for multiple return values... actually don't - # think we even use this but still!!! + # TODO: pyrna_enum2sphinx for multiple return values... actually don't + # think we even use this but still! type_descr = prop.get_type_description( as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID) descr = prop.description if not descr: descr = prop.name - # In rare cases descr may be empty + # In rare cases `descr` may be empty. fw(" `%s`, %s\n\n" % (prop.identifier, ", ".join((val for val in (descr, type_descr) if val)))) @@ -1493,7 +1542,7 @@ def pyrna2sphinx(basepath): fw("\n") - # Python methods + # Python methods. py_funcs = struct.get_py_functions() py_func = None @@ -1512,7 +1561,7 @@ def pyrna2sphinx(basepath): if struct.base or _BPY_STRUCT_FAKE: bases = list(reversed(struct.get_bases())) - # props + # Properties. del lines[:] if _BPY_STRUCT_FAKE: @@ -1543,7 +1592,7 @@ def pyrna2sphinx(basepath): fw(line) fw("\n") - # funcs + # Functions. del lines[:] if _BPY_STRUCT_FAKE: @@ -1598,7 +1647,7 @@ def pyrna2sphinx(basepath): if "bpy.types" not in EXCLUDE_MODULES: for struct in structs.values(): - # TODO, rna_info should filter these out! + # TODO: rna_info should filter these out! if "_OT_" in struct.identifier: continue write_struct(struct) @@ -1633,7 +1682,7 @@ def pyrna2sphinx(basepath): ] for key, descr in descr_items: - # GetSetDescriptorType, GetSetDescriptorType's are not documented yet + # `GetSetDescriptorType`, `GetSetDescriptorType` types are not documented yet. if type(descr) == MethodDescriptorType: py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key) @@ -1664,6 +1713,7 @@ def pyrna2sphinx(basepath): API_BASEURL_ADDON_CONTRIB = "https://developer.blender.org/diffusion/BAC" op_modules = {} + op = None for op in ops.values(): op_modules.setdefault(op.module_name, []).append(op) del op @@ -1688,7 +1738,7 @@ def pyrna2sphinx(basepath): # if the description isn't valid, we output the standard warning # with a link to the wiki so that people can help if not op.description or op.description == "(undocumented operator)": - operator_description = undocumented_message('bpy.ops', op.module_name, op.func_name) + operator_description = undocumented_message("bpy.ops", op.module_name, op.func_name) else: operator_description = op.description @@ -1717,9 +1767,9 @@ def pyrna2sphinx(basepath): def write_sphinx_conf_py(basepath): - ''' - Write sphinx's conf.py - ''' + """ + Write sphinx's ``conf.py``. + """ filepath = os.path.join(basepath, "conf.py") file = open(filepath, "w", encoding="utf-8") fw = file.write @@ -1808,17 +1858,10 @@ class PatchedPythonDomain(PythonDomain): file.close() -def execfile(filepath): - global_namespace = {"__file__": filepath, "__name__": "__main__"} - file_handle = open(filepath) - exec(compile(file_handle.read(), filepath, 'exec'), global_namespace) - file_handle.close() - - def write_rst_index(basepath): - ''' - Write the rst file of the main page, needed for sphinx (index.html) - ''' + """ + Write the RST file of the main page, needed for sphinx: ``index.html``. + """ filepath = os.path.join(basepath, "index.rst") file = open(filepath, "w", encoding="utf-8") fw = file.write @@ -1896,7 +1939,7 @@ def write_rst_index(basepath): fw("* :ref:`genindex`\n") fw("* :ref:`modindex`\n\n") - # special case, this 'bmesh.ops.rst' is extracted from C source + # Special case, this `bmesh.ops.rst` is extracted from C source. if "bmesh.ops" not in EXCLUDE_MODULES: execfile(os.path.join(SCRIPT_DIR, "rst_from_bmesh_opdefines.py")) @@ -1904,9 +1947,9 @@ def write_rst_index(basepath): def write_rst_bpy(basepath): - ''' - Write rst file of bpy module (disabled by default) - ''' + """ + Write RST file of ``bpy`` module (disabled by default) + """ if ARGS.bpy: filepath = os.path.join(basepath, "bpy.rst") file = open(filepath, "w", encoding="utf-8") @@ -1923,9 +1966,9 @@ def write_rst_bpy(basepath): def write_rst_types_index(basepath): - ''' - Write the rst file of bpy.types module (index) - ''' + """ + Write the RST file of ``bpy.types`` module (index) + """ if "bpy.types" not in EXCLUDE_MODULES: filepath = os.path.join(basepath, "bpy.types.rst") file = open(filepath, "w", encoding="utf-8") @@ -1939,9 +1982,9 @@ def write_rst_types_index(basepath): def write_rst_ops_index(basepath): - ''' - Write the rst file of bpy.ops module (index) - ''' + """ + Write the RST file of bpy.ops module (index) + """ if "bpy.ops" not in EXCLUDE_MODULES: filepath = os.path.join(basepath, "bpy.ops.rst") file = open(filepath, "w", encoding="utf-8") @@ -1958,7 +2001,7 @@ def write_rst_ops_index(basepath): def write_rst_msgbus(basepath): """ - Write the rst files of bpy.msgbus module + Write the RST files of ``bpy.msgbus`` module """ if 'bpy.msgbus' in EXCLUDE_MODULES: return @@ -1980,12 +2023,11 @@ def write_rst_msgbus(basepath): def write_rst_data(basepath): - ''' - Write the rst file of bpy.data module - ''' + """ + Write the RST file of ``bpy.data`` module. + """ if "bpy.data" not in EXCLUDE_MODULES: - # not actually a module, only write this file so we - # can reference in the TOC + # Not actually a module, only write this file so we can reference in the TOC. filepath = os.path.join(basepath, "bpy.data.rst") file = open(filepath, "w", encoding="utf-8") fw = file.write @@ -2007,9 +2049,9 @@ def write_rst_data(basepath): def write_rst_importable_modules(basepath): - ''' - Write the rst files of importable modules - ''' + """ + Write the RST files of importable modules. + """ importable_modules = { # Python_modules "bpy.path": "Path Utilities", @@ -2071,7 +2113,7 @@ def write_rst_importable_modules(basepath): # access such as `bpy.app.sdl` which doesn't seem useful since it hides more useful # module-like objects among library data access. importable_modules_parent_map = {} - for mod_name in importable_modules.keys(): + for mod_name in importable_modules: # Iterate over keys. if mod_name in EXCLUDE_MODULES: continue if "." in mod_name: @@ -2088,12 +2130,12 @@ def write_rst_importable_modules(basepath): def copy_handwritten_rsts(basepath): - # info docs + # Info docs. if not EXCLUDE_INFO_DOCS: - for info, info_desc in INFO_DOCS: + for info, _info_desc in INFO_DOCS: shutil.copy2(os.path.join(RST_DIR, info), basepath) - # TODO put this docs in Blender's code and use import as per modules above + # TODO: put this docs in Blender's code and use import as per modules above. handwritten_modules = [ "bgl", # "Blender OpenGl wrapper" "bmesh.ops", # generated by rst_from_bmesh_opdefines.py @@ -2104,13 +2146,13 @@ def copy_handwritten_rsts(basepath): for mod_name in handwritten_modules: if mod_name not in EXCLUDE_MODULES: - # copy2 keeps time/date stamps + # Copy2 keeps time/date stamps. shutil.copy2(os.path.join(RST_DIR, "%s.rst" % mod_name), basepath) - # changelog + # Change-log. shutil.copy2(os.path.join(RST_DIR, "change_log.rst"), basepath) - # copy images, could be smarter but just glob for now. + # Copy images, could be smarter but just glob for now. for f in os.listdir(RST_DIR): if f.endswith(".png"): shutil.copy2(os.path.join(RST_DIR, f), basepath) @@ -2132,12 +2174,16 @@ def copy_handwritten_extra(basepath): def copy_theme_assets(basepath): - shutil.copytree(os.path.join(SCRIPT_DIR, "static"), - os.path.join(basepath, "static"), - copy_function=shutil.copy) - shutil.copytree(os.path.join(SCRIPT_DIR, "templates"), - os.path.join(basepath, "templates"), - copy_function=shutil.copy) + shutil.copytree( + os.path.join(SCRIPT_DIR, "static"), + os.path.join(basepath, "static"), + copy_function=shutil.copy, + ) + shutil.copytree( + os.path.join(SCRIPT_DIR, "templates"), + os.path.join(basepath, "templates"), + copy_function=shutil.copy, + ) def rna2sphinx(basepath): @@ -2177,21 +2223,21 @@ def rna2sphinx(basepath): def align_sphinx_in_to_sphinx_in_tmp(dir_src, dir_dst): - ''' + """ Move changed files from SPHINX_IN_TMP to SPHINX_IN - ''' + """ import filecmp - # possible the dir doesn't exist when running recursively + # Possible the dir doesn't exist when running recursively. os.makedirs(dir_dst, exist_ok=True) sphinx_dst_files = set(os.listdir(dir_dst)) sphinx_src_files = set(os.listdir(dir_src)) - # remove deprecated files that have been removed + # Remove deprecated files that have been removed. for f in sorted(sphinx_dst_files): if f not in sphinx_src_files: - BPY_LOGGER.debug("\tdeprecated: %s" % f) + BPY_LOGGER.debug("\tdeprecated: %s", f) f_dst = os.path.join(dir_dst, f) if os.path.isdir(f_dst): shutil.rmtree(f_dst, True) @@ -2212,7 +2258,7 @@ def align_sphinx_in_to_sphinx_in_tmp(dir_src, dir_dst): do_copy = False if do_copy: - BPY_LOGGER.debug("\tupdating: %s" % f) + BPY_LOGGER.debug("\tupdating: %s", f) shutil.copy(f_src, f_dst) @@ -2234,10 +2280,7 @@ def refactor_sphinx_log(sphinx_logfile): def setup_monkey_patch(): filepath = os.path.join(SCRIPT_DIR, "sphinx_doc_gen_monkeypatch.py") - global_namespace = {"__file__": filepath, "__name__": "__main__"} - file = open(filepath, 'rb') - exec(compile(file.read(), filepath, 'exec'), global_namespace) - file.close() + execfile(filepath) # Avoid adding too many changes here. @@ -2270,109 +2313,119 @@ def main(): # Perform changes to Blender itself. setup_data = setup_blender() - # eventually, create the dirs + # Eventually, create the directories. for dir_path in [ARGS.output_dir, SPHINX_IN]: if not os.path.exists(dir_path): os.mkdir(dir_path) - # eventually, log in files + # Eventually, log in files. if ARGS.log: bpy_logfile = os.path.join(ARGS.output_dir, ".bpy.log") bpy_logfilehandler = logging.FileHandler(bpy_logfile, mode="w") bpy_logfilehandler.setLevel(logging.DEBUG) BPY_LOGGER.addHandler(bpy_logfilehandler) - # using a FileHandler seems to disable the stdout, so we add a StreamHandler + # using a `FileHandler` seems to disable the `stdout`, so we add a `StreamHandler`. bpy_log_stdout_handler = logging.StreamHandler(stream=sys.stdout) bpy_log_stdout_handler.setLevel(logging.DEBUG) BPY_LOGGER.addHandler(bpy_log_stdout_handler) - # in case of out-of-source build, copy the needed dirs + # In case of out-of-source build, copy the needed directories. if ARGS.output_dir != SCRIPT_DIR: - # examples dir + # Examples directory. examples_dir_copy = os.path.join(ARGS.output_dir, "examples") if os.path.exists(examples_dir_copy): shutil.rmtree(examples_dir_copy, True) - shutil.copytree(EXAMPLES_DIR, - examples_dir_copy, - ignore=shutil.ignore_patterns(*(".svn",)), - copy_function=shutil.copy) - - # dump the api in rst files + shutil.copytree( + EXAMPLES_DIR, + examples_dir_copy, + ignore=shutil.ignore_patterns(*(".svn",)), + copy_function=shutil.copy, + ) + + # Dump the API in RST files. if os.path.exists(SPHINX_IN_TMP): shutil.rmtree(SPHINX_IN_TMP, True) rna2sphinx(SPHINX_IN_TMP) if ARGS.full_rebuild: - # only for full updates + # Only for full updates. shutil.rmtree(SPHINX_IN, True) - shutil.copytree(SPHINX_IN_TMP, - SPHINX_IN, - copy_function=shutil.copy) + shutil.copytree( + SPHINX_IN_TMP, + SPHINX_IN, + copy_function=shutil.copy, + ) if ARGS.sphinx_build and os.path.exists(SPHINX_OUT): shutil.rmtree(SPHINX_OUT, True) if ARGS.sphinx_build_pdf and os.path.exists(SPHINX_OUT_PDF): shutil.rmtree(SPHINX_OUT_PDF, True) else: - # move changed files in SPHINX_IN + # Move changed files in `SPHINX_IN`. align_sphinx_in_to_sphinx_in_tmp(SPHINX_IN_TMP, SPHINX_IN) - # report which example files weren't used + # Report which example files weren't used. EXAMPLE_SET_UNUSED = EXAMPLE_SET - EXAMPLE_SET_USED if EXAMPLE_SET_UNUSED: - BPY_LOGGER.debug("\nUnused examples found in '%s'..." % EXAMPLES_DIR) + BPY_LOGGER.debug("\nUnused examples found in '%s'...", EXAMPLES_DIR) for f in sorted(EXAMPLE_SET_UNUSED): - BPY_LOGGER.debug(" %s.py" % f) - BPY_LOGGER.debug(" %d total\n" % len(EXAMPLE_SET_UNUSED)) + BPY_LOGGER.debug(" %s.py", f) + BPY_LOGGER.debug(" %d total\n", len(EXAMPLE_SET_UNUSED)) - # eventually, build the html docs + # Eventually, build the html docs. if ARGS.sphinx_build: import subprocess subprocess.call(SPHINX_BUILD) - # sphinx-build log cleanup+sort + # Sphinx-build log cleanup+sort. if ARGS.log: if os.stat(SPHINX_BUILD_LOG).st_size: refactor_sphinx_log(SPHINX_BUILD_LOG) - # eventually, build the pdf docs + # Eventually, build the PDF docs. if ARGS.sphinx_build_pdf: import subprocess subprocess.call(SPHINX_BUILD_PDF) subprocess.call(SPHINX_MAKE_PDF, stdout=SPHINX_MAKE_PDF_STDOUT) - # sphinx-build log cleanup+sort + # Sphinx-build log cleanup+sort. if ARGS.log: if os.stat(SPHINX_BUILD_PDF_LOG).st_size: refactor_sphinx_log(SPHINX_BUILD_PDF_LOG) - # eventually, prepare the dir to be deployed online (REFERENCE_PATH) + # Eventually, prepare the dir to be deployed online (REFERENCE_PATH). if ARGS.pack_reference: if ARGS.sphinx_build: - # delete REFERENCE_PATH + # Delete REFERENCE_PATH. if os.path.exists(REFERENCE_PATH): shutil.rmtree(REFERENCE_PATH, True) - # copy SPHINX_OUT to the REFERENCE_PATH - ignores = ('.doctrees', '.buildinfo') - shutil.copytree(SPHINX_OUT, - REFERENCE_PATH, - ignore=shutil.ignore_patterns(*ignores)) + # Copy SPHINX_OUT to the REFERENCE_PATH. + ignores = (".doctrees", ".buildinfo") + shutil.copytree( + SPHINX_OUT, + REFERENCE_PATH, + ignore=shutil.ignore_patterns(*ignores), + ) - # zip REFERENCE_PATH + # Zip REFERENCE_PATH. basename = os.path.join(ARGS.output_dir, REFERENCE_NAME) - tmp_path = shutil.make_archive(basename, 'zip', - root_dir=ARGS.output_dir, - base_dir=REFERENCE_NAME) + tmp_path = shutil.make_archive( + basename, "zip", + root_dir=ARGS.output_dir, + base_dir=REFERENCE_NAME, + ) final_path = os.path.join(REFERENCE_PATH, BLENDER_ZIP_FILENAME) os.rename(tmp_path, final_path) if ARGS.sphinx_build_pdf: - # copy the pdf to REFERENCE_PATH - shutil.copy(os.path.join(SPHINX_OUT_PDF, "contents.pdf"), - os.path.join(REFERENCE_PATH, BLENDER_PDF_FILENAME)) + # Copy the pdf to REFERENCE_PATH. + shutil.copy( + os.path.join(SPHINX_OUT_PDF, "contents.pdf"), + os.path.join(REFERENCE_PATH, BLENDER_PDF_FILENAME), + ) teardown_blender(setup_data) diff --git a/intern/atomic/atomic_ops.h b/intern/atomic/atomic_ops.h index 6a4d6d263c0..2bedce1b4f0 100644 --- a/intern/atomic/atomic_ops.h +++ b/intern/atomic/atomic_ops.h @@ -64,16 +64,22 @@ ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x); ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x); ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x); ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new); +ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v); +ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v); ATOMIC_INLINE int64_t atomic_add_and_fetch_int64(int64_t *p, int64_t x); ATOMIC_INLINE int64_t atomic_sub_and_fetch_int64(int64_t *p, int64_t x); ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x); ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x); ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new); +ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v); +ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v); ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x); ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x); ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new); +ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v); +ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v); ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x); ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x); @@ -82,6 +88,8 @@ ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x); ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x); ATOMIC_INLINE int32_t atomic_sub_and_fetch_int32(int32_t *p, int32_t x); ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new); +ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v); +ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v); ATOMIC_INLINE int32_t atomic_fetch_and_add_int32(int32_t *p, int32_t x); ATOMIC_INLINE int32_t atomic_fetch_and_or_int32(int32_t *p, int32_t x); @@ -104,6 +112,8 @@ ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x); ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x); ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x); ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new); +ATOMIC_INLINE size_t atomic_load_z(const size_t *v); +ATOMIC_INLINE void atomic_store_z(size_t *p, size_t v); /* Uses CAS loop, see warning below. */ ATOMIC_INLINE size_t atomic_fetch_and_update_max_z(size_t *p, size_t x); diff --git a/intern/atomic/intern/atomic_ops_ext.h b/intern/atomic/intern/atomic_ops_ext.h index aedf0985169..6ecc47f18be 100644 --- a/intern/atomic/intern/atomic_ops_ext.h +++ b/intern/atomic/intern/atomic_ops_ext.h @@ -102,6 +102,24 @@ ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new) #endif } +ATOMIC_INLINE size_t atomic_load_z(const size_t *v) +{ +#if (LG_SIZEOF_PTR == 8) + return (size_t)atomic_load_uint64((const uint64_t *)v); +#elif (LG_SIZEOF_PTR == 4) + return (size_t)atomic_load_uint32((const uint32_t *)v); +#endif +} + +ATOMIC_INLINE void atomic_store_z(size_t *p, size_t v) +{ +#if (LG_SIZEOF_PTR == 8) + atomic_store_uint64((uint64_t *)p, v); +#elif (LG_SIZEOF_PTR == 4) + atomic_store_uint32((uint32_t *)p, v); +#endif +} + ATOMIC_INLINE size_t atomic_fetch_and_update_max_z(size_t *p, size_t x) { size_t prev_value; diff --git a/intern/atomic/intern/atomic_ops_msvc.h b/intern/atomic/intern/atomic_ops_msvc.h index ea5ae666db9..e65691d3970 100644 --- a/intern/atomic/intern/atomic_ops_msvc.h +++ b/intern/atomic/intern/atomic_ops_msvc.h @@ -49,6 +49,16 @@ # pragma GCC diagnostic ignored "-Wincompatible-pointer-types" #endif +/* TODO(sergey): On x64 platform both read and write of a variable aligned to its type size is + * atomic, so in theory it is possible to avoid memory barrier and gain performance. The downside + * of that would be that it will impose requirement to value which is being operated on. */ +#define __atomic_impl_load_generic(v) (MemoryBarrier(), *(v)) +#define __atomic_impl_store_generic(p, v) \ + do { \ + *(p) = (v); \ + MemoryBarrier(); \ + } while (0) + /* 64-bit operations. */ /* Unsigned */ ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x) @@ -66,6 +76,16 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne return InterlockedCompareExchange64((int64_t *)v, _new, old); } +ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v) +{ + return __atomic_impl_load_generic(v); +} + +ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v) +{ + __atomic_impl_store_generic(p, v); +} + ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x) { return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); @@ -92,6 +112,16 @@ ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new) return InterlockedCompareExchange64(v, _new, old); } +ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v) +{ + return __atomic_impl_load_generic(v); +} + +ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v) +{ + __atomic_impl_store_generic(p, v); +} + ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x) { return InterlockedExchangeAdd64(p, x); @@ -120,6 +150,16 @@ ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _ne return InterlockedCompareExchange((long *)v, _new, old); } +ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v) +{ + return __atomic_impl_load_generic(v); +} + +ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v) +{ + __atomic_impl_store_generic(p, v); +} + ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x) { return InterlockedExchangeAdd(p, x); @@ -151,6 +191,16 @@ ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new) return InterlockedCompareExchange((long *)v, _new, old); } +ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v) +{ + return __atomic_impl_load_generic(v); +} + +ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v) +{ + __atomic_impl_store_generic(p, v); +} + ATOMIC_INLINE int32_t atomic_fetch_and_add_int32(int32_t *p, int32_t x) { return InterlockedExchangeAdd((long *)p, x); @@ -225,6 +275,9 @@ ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b) #endif } +#undef __atomic_impl_load_generic +#undef __atomic_impl_store_generic + #if defined(__clang__) # pragma GCC diagnostic pop #endif diff --git a/intern/atomic/intern/atomic_ops_unix.h b/intern/atomic/intern/atomic_ops_unix.h index 2fcfe34d03c..8c703fc4a8d 100644 --- a/intern/atomic/intern/atomic_ops_unix.h +++ b/intern/atomic/intern/atomic_ops_unix.h @@ -99,6 +99,22 @@ ATOMIC_INLINE void atomic_spin_unlock(volatile AtomicSpinLock *lock) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Common part of x64 implementation + * \{ */ + +/* TODO(sergey): On x64 platform both read and write of a variable aligned to its type size is + * atomic, so in theory it is possible to avoid memory barrier and gain performance. The downside + * of that would be that it will impose requirement to value which is being operated on. */ +#define __atomic_impl_load_generic(v) (__sync_synchronize(), *(v)) +#define __atomic_impl_store_generic(p, v) \ + do { \ + *(p) = (v); \ + __sync_synchronize(); \ + } while (0) + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Common part of locking fallback implementation * \{ */ @@ -158,6 +174,23 @@ static _ATOMIC_MAYBE_UNUSED AtomicSpinLock _atomic_global_lock = {0}; return original_value; \ } +#define ATOMIC_LOCKING_LOAD_DEFINE(_type) \ + ATOMIC_INLINE _type##_t atomic_load_##_type(const _type##_t *v) \ + { \ + atomic_spin_lock(&_atomic_global_lock); \ + const _type##_t value = *v; \ + atomic_spin_unlock(&_atomic_global_lock); \ + return value; \ + } + +#define ATOMIC_LOCKING_STORE_DEFINE(_type) \ + ATOMIC_INLINE void atomic_store_##_type(_type##_t *p, const _type##_t v) \ + { \ + atomic_spin_lock(&_atomic_global_lock); \ + *p = v; \ + atomic_spin_unlock(&_atomic_global_lock); \ + } + /** \} */ /* -------------------------------------------------------------------- */ @@ -192,6 +225,16 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne return __sync_val_compare_and_swap(v, old, _new); } +ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v) +{ + return __atomic_load_n(v, __ATOMIC_SEQ_CST); +} + +ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v) +{ + __atomic_store(p, &v, __ATOMIC_SEQ_CST); +} + /* Signed */ ATOMIC_INLINE int64_t atomic_add_and_fetch_int64(int64_t *p, int64_t x) { @@ -218,6 +261,16 @@ ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new) return __sync_val_compare_and_swap(v, old, _new); } +ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v) +{ + return __atomic_load_n(v, __ATOMIC_SEQ_CST); +} + +ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v) +{ + __atomic_store(p, &v, __ATOMIC_SEQ_CST); +} + #elif !defined(ATOMIC_FORCE_USE_FALLBACK) && (defined(__amd64__) || defined(__x86_64__)) /* Unsigned */ ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x) @@ -256,6 +309,16 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne return ret; } +ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v) +{ + return __atomic_impl_load_generic(v); +} + +ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v) +{ + __atomic_impl_store_generic(p, v); +} + /* Signed */ ATOMIC_INLINE int64_t atomic_fetch_and_add_int64(int64_t *p, int64_t x) { @@ -292,6 +355,17 @@ ATOMIC_INLINE int64_t atomic_cas_int64(int64_t *v, int64_t old, int64_t _new) asm volatile("lock; cmpxchgq %2,%1" : "=a"(ret), "+m"(*v) : "r"(_new), "0"(old) : "memory"); return ret; } + +ATOMIC_INLINE int64_t atomic_load_int64(const int64_t *v) +{ + return __atomic_impl_load_generic(v); +} + +ATOMIC_INLINE void atomic_store_int64(int64_t *p, int64_t v) +{ + __atomic_impl_store_generic(p, v); +} + #else /* Unsigned */ @@ -304,6 +378,9 @@ ATOMIC_LOCKING_FETCH_AND_SUB_DEFINE(uint64) ATOMIC_LOCKING_CAS_DEFINE(uint64) +ATOMIC_LOCKING_LOAD_DEFINE(uint64) +ATOMIC_LOCKING_STORE_DEFINE(uint64) + /* Signed */ ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(int64) ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(int64) @@ -313,6 +390,9 @@ ATOMIC_LOCKING_FETCH_AND_SUB_DEFINE(int64) ATOMIC_LOCKING_CAS_DEFINE(int64) +ATOMIC_LOCKING_LOAD_DEFINE(int64) +ATOMIC_LOCKING_STORE_DEFINE(int64) + #endif /** \} */ @@ -339,6 +419,16 @@ ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _ne return __sync_val_compare_and_swap(v, old, _new); } +ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v) +{ + return __atomic_load_n(v, __ATOMIC_SEQ_CST); +} + +ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v) +{ + __atomic_store(p, &v, __ATOMIC_SEQ_CST); +} + /* Signed */ ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x) { @@ -355,6 +445,16 @@ ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new) return __sync_val_compare_and_swap(v, old, _new); } +ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v) +{ + return __atomic_load_n(v, __ATOMIC_SEQ_CST); +} + +ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v) +{ + __atomic_store(p, &v, __ATOMIC_SEQ_CST); +} + #elif !defined(ATOMIC_FORCE_USE_FALLBACK) && \ (defined(__i386__) || defined(__amd64__) || defined(__x86_64__)) /* Unsigned */ @@ -385,6 +485,16 @@ ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _ne return ret; } +ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v) +{ + return __atomic_load_n(v, __ATOMIC_SEQ_CST); +} + +ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v) +{ + __atomic_store(p, &v, __ATOMIC_SEQ_CST); +} + /* Signed */ ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x) { @@ -413,6 +523,16 @@ ATOMIC_INLINE int32_t atomic_cas_int32(int32_t *v, int32_t old, int32_t _new) return ret; } +ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v) +{ + return __atomic_load_n(v, __ATOMIC_SEQ_CST); +} + +ATOMIC_INLINE void atomic_store_int32(int32_t *p, int32_t v) +{ + __atomic_store(p, &v, __ATOMIC_SEQ_CST); +} + #else /* Unsigned */ @@ -422,6 +542,9 @@ ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(uint32) ATOMIC_LOCKING_CAS_DEFINE(uint32) +ATOMIC_LOCKING_LOAD_DEFINE(uint32) +ATOMIC_LOCKING_STORE_DEFINE(uint32) + /* Signed */ ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE(int32) @@ -429,6 +552,9 @@ ATOMIC_LOCKING_SUB_AND_FETCH_DEFINE(int32) ATOMIC_LOCKING_CAS_DEFINE(int32) +ATOMIC_LOCKING_LOAD_DEFINE(int32) +ATOMIC_LOCKING_STORE_DEFINE(int32) + #endif #if !defined(ATOMIC_FORCE_USE_FALLBACK) && \ @@ -548,6 +674,9 @@ ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(int8) /** \} */ +#undef __atomic_impl_load_generic +#undef __atomic_impl_store_generic + #undef ATOMIC_LOCKING_OP_AND_FETCH_DEFINE #undef ATOMIC_LOCKING_FETCH_AND_OP_DEFINE #undef ATOMIC_LOCKING_ADD_AND_FETCH_DEFINE @@ -557,5 +686,7 @@ ATOMIC_LOCKING_FETCH_AND_OR_DEFINE(int8) #undef ATOMIC_LOCKING_FETCH_AND_OR_DEFINE #undef ATOMIC_LOCKING_FETCH_AND_AND_DEFINE #undef ATOMIC_LOCKING_CAS_DEFINE +#undef ATOMIC_LOCKING_LOAD_DEFINE +#undef ATOMIC_LOCKING_STORE_DEFINE #endif /* __ATOMIC_OPS_UNIX_H__ */ diff --git a/intern/atomic/tests/atomic_test.cc b/intern/atomic/tests/atomic_test.cc index d79374416ec..ee06085c95d 100644 --- a/intern/atomic/tests/atomic_test.cc +++ b/intern/atomic/tests/atomic_test.cc @@ -143,6 +143,40 @@ TEST(atomic, atomic_cas_uint64) } } +TEST(atomic, atomic_load_uint64) +{ + /* Make sure alias is implemented. */ + { + uint64_t value = 2; + EXPECT_EQ(atomic_load_uint64(&value), 2); + } + + /* Make sure alias is using proper bitness. */ + { + const uint64_t uint64_t_max = std::numeric_limits<uint64_t>::max(); + uint64_t value = uint64_t_max; + EXPECT_EQ(atomic_load_uint64(&value), uint64_t_max); + } +} + +TEST(atomic, atomic_store_uint64) +{ + /* Make sure alias is implemented. */ + { + uint64_t value = 0; + atomic_store_uint64(&value, 2); + EXPECT_EQ(value, 2); + } + + /* Make sure alias is using proper bitness. */ + { + const uint64_t uint64_t_max = std::numeric_limits<uint64_t>::max(); + uint64_t value = 0; + atomic_store_uint64(&value, uint64_t_max); + EXPECT_EQ(value, uint64_t_max); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -277,6 +311,40 @@ TEST(atomic, atomic_cas_int64) } } +TEST(atomic, atomic_load_int64) +{ + /* Make sure alias is implemented. */ + { + int64_t value = 2; + EXPECT_EQ(atomic_load_int64(&value), 2); + } + + /* Make sure alias is using proper bitness. */ + { + const int64_t int64_t_max = std::numeric_limits<int64_t>::max(); + int64_t value = int64_t_max; + EXPECT_EQ(atomic_load_int64(&value), int64_t_max); + } +} + +TEST(atomic, atomic_store_int64) +{ + /* Make sure alias is implemented. */ + { + int64_t value = 0; + atomic_store_int64(&value, 2); + EXPECT_EQ(value, 2); + } + + /* Make sure alias is using proper bitness. */ + { + const int64_t int64_t_max = std::numeric_limits<int64_t>::max(); + int64_t value = 0; + atomic_store_int64(&value, int64_t_max); + EXPECT_EQ(value, int64_t_max); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -358,6 +426,40 @@ TEST(atomic, atomic_cas_uint32) } } +TEST(atomic, atomic_load_uint32) +{ + /* Make sure alias is implemented. */ + { + uint32_t value = 2; + EXPECT_EQ(atomic_load_uint32(&value), 2); + } + + /* Make sure alias is using proper bitness. */ + { + const uint32_t uint32_t_max = std::numeric_limits<uint32_t>::max(); + uint32_t value = uint32_t_max; + EXPECT_EQ(atomic_load_uint32(&value), uint32_t_max); + } +} + +TEST(atomic, atomic_store_uint32) +{ + /* Make sure alias is implemented. */ + { + uint32_t value = 0; + atomic_store_uint32(&value, 2); + EXPECT_EQ(value, 2); + } + + /* Make sure alias is using proper bitness. */ + { + const uint32_t uint32_t_max = std::numeric_limits<uint32_t>::max(); + uint32_t value = 0; + atomic_store_uint32(&value, uint32_t_max); + EXPECT_EQ(value, uint32_t_max); + } +} + TEST(atomic, atomic_fetch_and_add_uint32) { { @@ -505,6 +607,40 @@ TEST(atomic, atomic_cas_int32) } } +TEST(atomic, atomic_load_int32) +{ + /* Make sure alias is implemented. */ + { + int32_t value = 2; + EXPECT_EQ(atomic_load_int32(&value), 2); + } + + /* Make sure alias is using proper bitness. */ + { + const int32_t int32_t_max = std::numeric_limits<int32_t>::max(); + int32_t value = int32_t_max; + EXPECT_EQ(atomic_load_int32(&value), int32_t_max); + } +} + +TEST(atomic, atomic_store_int32) +{ + /* Make sure alias is implemented. */ + { + int32_t value = 0; + atomic_store_int32(&value, 2); + EXPECT_EQ(value, 2); + } + + /* Make sure alias is using proper bitness. */ + { + const int32_t int32_t_max = std::numeric_limits<int32_t>::max(); + int32_t value = 0; + atomic_store_int32(&value, int32_t_max); + EXPECT_EQ(value, int32_t_max); + } +} + TEST(atomic, atomic_fetch_and_add_int32) { { @@ -761,6 +897,40 @@ TEST(atomic, atomic_cas_z) } } +TEST(atomic, atomic_load_z) +{ + /* Make sure alias is implemented. */ + { + size_t value = 2; + EXPECT_EQ(atomic_load_z(&value), 2); + } + + /* Make sure alias is using proper bitness. */ + { + const size_t size_t_max = std::numeric_limits<size_t>::max(); + size_t value = size_t_max; + EXPECT_EQ(atomic_load_z(&value), size_t_max); + } +} + +TEST(atomic, atomic_store_z) +{ + /* Make sure alias is implemented. */ + { + size_t value = 0; + atomic_store_z(&value, 2); + EXPECT_EQ(value, 2); + } + + /* Make sure alias is using proper bitness. */ + { + const size_t size_t_max = std::numeric_limits<size_t>::max(); + size_t value = 0; + atomic_store_z(&value, size_t_max); + EXPECT_EQ(value, size_t_max); + } +} + TEST(atomic, atomic_fetch_and_update_max_z) { const size_t size_t_max = std::numeric_limits<size_t>::max(); diff --git a/intern/cycles/blender/volume.cpp b/intern/cycles/blender/volume.cpp index 8dd2d45c0b6..a9a2c474f40 100644 --- a/intern/cycles/blender/volume.cpp +++ b/intern/cycles/blender/volume.cpp @@ -219,7 +219,10 @@ static void sync_smoke_volume( class BlenderVolumeLoader : public VDBImageLoader { public: - BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name) + BlenderVolumeLoader(BL::BlendData &b_data, + BL::Volume &b_volume, + const string &grid_name, + BL::VolumeRender::precision_enum precision_) : VDBImageLoader(grid_name), b_volume(b_volume) { b_volume.grids.load(b_data.ptr.data); @@ -241,6 +244,20 @@ class BlenderVolumeLoader : public VDBImageLoader { } } #endif +#ifdef WITH_NANOVDB + switch (precision_) { + case BL::VolumeRender::precision_FULL: + precision = 32; + break; + case BL::VolumeRender::precision_HALF: + precision = 16; + break; + default: + case BL::VolumeRender::precision_VARIABLE: + precision = 0; + break; + } +#endif } BL::Volume b_volume; @@ -318,7 +335,8 @@ static void sync_volume_object(BL::BlendData &b_data, volume->attributes.add(std) : volume->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL); - ImageLoader *loader = new BlenderVolumeLoader(b_data, b_volume, name.string()); + ImageLoader *loader = new BlenderVolumeLoader( + b_data, b_volume, name.string(), b_render.precision()); ImageParams params; params.frame = b_volume.grids.frame(); diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index 6908ae5ead3..75177566901 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -1084,7 +1084,9 @@ void CUDADevice::tex_alloc(device_texture &mem) need_texture_info = true; if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FPN && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FP16) { CUDA_RESOURCE_DESC resDesc; memset(&resDesc, 0, sizeof(resDesc)); diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index 7159277b325..f8fdb86ca29 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -1042,7 +1042,9 @@ void HIPDevice::tex_alloc(device_texture &mem) need_texture_info = true; if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FPN && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FP16) { /* Bindless textures. */ hipResourceDesc resDesc; memset(&resDesc, 0, sizeof(resDesc)); diff --git a/intern/cycles/device/memory.cpp b/intern/cycles/device/memory.cpp index 4c068dbdd3e..40cf2573cfb 100644 --- a/intern/cycles/device/memory.cpp +++ b/intern/cycles/device/memory.cpp @@ -165,6 +165,8 @@ device_texture::device_texture(Device *device, case IMAGE_DATA_TYPE_BYTE: case IMAGE_DATA_TYPE_NANOVDB_FLOAT: case IMAGE_DATA_TYPE_NANOVDB_FLOAT3: + case IMAGE_DATA_TYPE_NANOVDB_FPN: + case IMAGE_DATA_TYPE_NANOVDB_FP16: data_type = TYPE_UCHAR; data_elements = 1; break; diff --git a/intern/cycles/kernel/device/cpu/image.h b/intern/cycles/kernel/device/cpu/image.h index 3b714a3e580..7809ec5f4a7 100644 --- a/intern/cycles/kernel/device/cpu/image.h +++ b/intern/cycles/kernel/device/cpu/image.h @@ -817,6 +817,16 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, } case IMAGE_DATA_TYPE_NANOVDB_FLOAT3: return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp); + case IMAGE_DATA_TYPE_NANOVDB_FPN: { + const float f = NanoVDBInterpolator<nanovdb::FpN, float>::interp_3d( + info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } + case IMAGE_DATA_TYPE_NANOVDB_FP16: { + const float f = NanoVDBInterpolator<nanovdb::Fp16, float>::interp_3d( + info, P.x, P.y, P.z, interp); + return make_float4(f, f, f, 1.0f); + } #endif default: assert(0); diff --git a/intern/cycles/kernel/device/gpu/image.h b/intern/cycles/kernel/device/gpu/image.h index c5bc7d88e02..29d851ae478 100644 --- a/intern/cycles/kernel/device/gpu/image.h +++ b/intern/cycles/kernel/device/gpu/image.h @@ -125,7 +125,8 @@ kernel_tex_image_interp_tricubic(ccl_global const TextureInfo &info, float x, fl #ifdef WITH_NANOVDB template<typename T, typename S> -ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, float z) +ccl_device typename nanovdb::NanoGrid<T>::ValueType kernel_tex_image_interp_tricubic_nanovdb( + S &s, float x, float y, float z) { float px = floorf(x); float py = floorf(y); @@ -157,7 +158,7 @@ ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, fl } template<typename T> -ccl_device_noinline T kernel_tex_image_interp_nanovdb( +ccl_device_noinline typename nanovdb::NanoGrid<T>::ValueType kernel_tex_image_interp_nanovdb( ccl_global const TextureInfo &info, float x, float y, float z, uint interpolation) { using namespace nanovdb; @@ -238,6 +239,14 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, info, x, y, z, interpolation); return make_float4(f[0], f[1], f[2], 1.0f); } + if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FPN) { + float f = kernel_tex_image_interp_nanovdb<nanovdb::FpN>(info, x, y, z, interpolation); + return make_float4(f, f, f, 1.0f); + } + if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FP16) { + float f = kernel_tex_image_interp_nanovdb<nanovdb::Fp16>(info, x, y, z, interpolation); + return make_float4(f, f, f, 1.0f); + } #endif if (texture_type == IMAGE_DATA_TYPE_FLOAT4 || texture_type == IMAGE_DATA_TYPE_BYTE4 || texture_type == IMAGE_DATA_TYPE_HALF4 || texture_type == IMAGE_DATA_TYPE_USHORT4) { diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index 82b51843864..328c58e7905 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -647,8 +647,9 @@ ccl_device_inline void kernel_gpu_film_convert_half_write(ccl_global uchar4 *rgb const int x = render_pixel_index % width; \ const int y = render_pixel_index / width; \ \ - ccl_global const float *buffer = render_buffer + offset + x * kfilm_convert.pass_stride + \ - y * stride * kfilm_convert.pass_stride; \ + const uint64_t buffer_pixel_index = x + y * stride; \ + ccl_global const float *buffer = render_buffer + offset + \ + buffer_pixel_index * kfilm_convert.pass_stride; \ \ ccl_global float *pixel = pixels + \ (render_pixel_index + rgba_offset) * kfilm_convert.pixel_stride; \ @@ -677,8 +678,9 @@ ccl_device_inline void kernel_gpu_film_convert_half_write(ccl_global uchar4 *rgb const int x = render_pixel_index % width; \ const int y = render_pixel_index / width; \ \ - ccl_global const float *buffer = render_buffer + offset + x * kfilm_convert.pass_stride + \ - y * stride * kfilm_convert.pass_stride; \ + const uint64_t buffer_pixel_index = x + y * stride; \ + ccl_global const float *buffer = render_buffer + offset + \ + buffer_pixel_index * kfilm_convert.pass_stride; \ \ float pixel[4]; \ film_get_pass_pixel_##variant(&kfilm_convert, buffer, pixel); \ diff --git a/intern/cycles/kernel/film/adaptive_sampling.h b/intern/cycles/kernel/film/adaptive_sampling.h index ad9b3b08ac5..16867c39d99 100644 --- a/intern/cycles/kernel/film/adaptive_sampling.h +++ b/intern/cycles/kernel/film/adaptive_sampling.h @@ -91,13 +91,13 @@ ccl_device void kernel_adaptive_sampling_filter_x(KernelGlobals kg, bool prev = false; for (int x = start_x; x < start_x + width; ++x) { int index = offset + x + y * stride; - ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride; const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; if (buffer[aux_w_offset] == 0.0f) { if (x > start_x && !prev) { index = index - 1; - buffer = render_buffer + index * kernel_data.film.pass_stride; + buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride; buffer[aux_w_offset] = 0.0f; } prev = true; @@ -124,13 +124,13 @@ ccl_device void kernel_adaptive_sampling_filter_y(KernelGlobals kg, bool prev = false; for (int y = start_y; y < start_y + height; ++y) { int index = offset + x + y * stride; - ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride; const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; if (buffer[aux_w_offset] == 0.0f) { if (y > start_y && !prev) { index = index - stride; - buffer = render_buffer + index * kernel_data.film.pass_stride; + buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride; buffer[aux_w_offset] = 0.0f; } prev = true; diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index 3772db845a8..293c1d243f8 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -102,7 +102,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, /* Setup render buffers. */ const int index = INTEGRATOR_STATE(state, path, render_pixel_index); const int pass_stride = kernel_data.film.pass_stride; - ccl_global float *buffer = render_buffer + index * pass_stride; + ccl_global float *buffer = render_buffer + (uint64_t)index * pass_stride; ccl_global float *primitive = buffer + kernel_data.film.pass_bake_primitive; ccl_global float *differential = buffer + kernel_data.film.pass_bake_differential; diff --git a/intern/cycles/kernel/svm/blackbody.h b/intern/cycles/kernel/svm/blackbody.h index af59c2fe747..774fa16b384 100644 --- a/intern/cycles/kernel/svm/blackbody.h +++ b/intern/cycles/kernel/svm/blackbody.h @@ -24,6 +24,7 @@ ccl_device_noinline void svm_node_blackbody(KernelGlobals kg, float temperature = stack_load_float(stack, temperature_offset); float3 color_rgb = rec709_to_rgb(kg, svm_math_blackbody_color_rec709(temperature)); + color_rgb = max(color_rgb, zero_float3()); stack_store_float3(stack, col_offset, color_rgb); } diff --git a/intern/cycles/kernel/svm/math_util.h b/intern/cycles/kernel/svm/math_util.h index 9f2d9561e26..89bd4a501a7 100644 --- a/intern/cycles/kernel/svm/math_util.h +++ b/intern/cycles/kernel/svm/math_util.h @@ -192,28 +192,26 @@ ccl_device float svm_math(NodeMathType type, float a, float b, float c) ccl_device float3 svm_math_blackbody_color_rec709(float t) { /* Calculate color in range 800..12000 using an approximation - * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B - * Max absolute error for RGB is (0.00095, 0.00077, 0.00057), - * which is enough to get the same 8 bit/channel color. - */ + * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B. + * + * The result of this can be negative to support gamut wider than + * than rec.709, just needs to be clamped. */ if (t >= 12000.0f) { - return make_float3(0.826270103f, 0.994478524f, 1.56626022f); + return make_float3(0.8262954810464208f, 0.9945080501520986f, 1.566307710274283f); } - else if (t < 965.0f) { - /* For 800 <= t < 965 color does not change in OSL implementation, so keep color the same */ - return make_float3(4.70366907f, 0.0f, 0.0f); + else if (t < 800.0f) { + /* Arbitrary lower limit where light is very dim, matching OSL. */ + return make_float3(5.413294490189271f, -0.20319390035873933f, -0.0822535242887164f); } - /* Manually align for readability. */ - /* clang-format off */ - int i = (t >= 6365.0f) ? 5 : - (t >= 3315.0f) ? 4 : - (t >= 1902.0f) ? 3 : - (t >= 1449.0f) ? 2 : - (t >= 1167.0f) ? 1 : + int i = (t >= 6365.0f) ? 6 : + (t >= 3315.0f) ? 5 : + (t >= 1902.0f) ? 4 : + (t >= 1449.0f) ? 3 : + (t >= 1167.0f) ? 2 : + (t >= 965.0f) ? 1 : 0; - /* clang-format on */ ccl_constant float *r = blackbody_table_r[i]; ccl_constant float *g = blackbody_table_g[i]; diff --git a/intern/cycles/kernel/tables.h b/intern/cycles/kernel/tables.h index f826cc5c5ef..c1fdbba3fa7 100644 --- a/intern/cycles/kernel/tables.h +++ b/intern/cycles/kernel/tables.h @@ -4,30 +4,33 @@ /* clang-format off */ ccl_inline_constant float blackbody_table_r[][3] = { - {2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f}, - {3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f}, - {4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f}, - {4.66849800e+03f, 2.85655028e-05f, 1.29075375e-01f}, - {4.60124770e+03f, 2.89727618e-05f, 1.48001316e-01f}, - {3.78765709e+03f, 9.36026367e-06f, 3.98995841e-01f} + {1.61919106e+03f, -2.05010916e-03f, 5.02995757e+00f}, + {2.48845471e+03f, -1.11330907e-03f, 3.22621544e+00f}, + {3.34143193e+03f, -4.86551192e-04f, 1.76486769e+00f}, + {4.09461742e+03f, -1.27446582e-04f, 7.25731635e-01f}, + {4.67028036e+03f, 2.91258199e-05f, 1.26703442e-01f}, + {4.59509185e+03f, 2.87495649e-05f, 1.50345020e-01f}, + {3.78717450e+03f, 9.35907826e-06f, 3.99075871e-01f} }; ccl_inline_constant float blackbody_table_g[][3] = { - {-7.50343014e+02f, 3.15679613e-04f, 4.73464526e-01f}, - {-1.00402363e+03f, 1.29189794e-04f, 9.08181524e-01f}, - {-1.22075471e+03f, 2.56245413e-05f, 1.20753416e+00f}, - {-1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f}, - {-1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f}, - {-5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f} + {-4.88999748e+02f, 6.04330754e-04f, -7.55807526e-02f}, + {-7.55994277e+02f, 3.16730098e-04f, 4.78306139e-01f}, + {-1.02363977e+03f, 1.20223470e-04f, 9.36662319e-01f}, + {-1.26571316e+03f, 4.87340896e-06f, 1.27054498e+00f}, + {-1.42529332e+03f, -4.01150431e-05f, 1.43972784e+00f}, + {-1.17554822e+03f, -2.16378048e-05f, 1.30408023e+00f}, + {-5.00799571e+02f, -4.59832026e-06f, 1.09098763e+00f} }; ccl_inline_constant float blackbody_table_b[][4] = { - {0.0f, 0.0f, 0.0f, 0.0f}, /* zeros should be optimized by compiler */ - {0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, - {-2.02524603e-11f, 1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f}, - {-2.22463426e-13f, -1.55078698e-08f, 3.81675160e-04f, -7.30646033e-01f}, - {6.72595954e-13f, -2.73059993e-08f, 4.24068546e-04f, -7.52204323e-01f} + {5.96945309e-11f, -4.85742887e-08f, -9.70622247e-05f, -4.07936148e-03f}, + {2.40430366e-11f, 5.55021075e-08f, -1.98503712e-04f, 2.89312858e-02f}, + {-1.40949732e-11f, 1.89878968e-07f, -3.56632824e-04f, 9.10767778e-02f}, + {-3.61460868e-11f, 2.84822009e-07f, -4.93211319e-04f, 1.56723440e-01f}, + {-1.97075738e-11f, 1.75359352e-07f, -2.50542825e-04f, -2.22783266e-02f}, + {-1.61997957e-13f, -1.64216008e-08f, 3.86216271e-04f, -7.38077418e-01f}, + {6.72650283e-13f, -2.73078809e-08f, 4.24098264e-04f, -7.52335691e-01f} }; ccl_inline_constant float cie_colour_match[][3] = { diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp index c61ad1f1d71..2aa9a6bc1a1 100644 --- a/intern/cycles/scene/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -64,6 +64,10 @@ const char *name_from_type(ImageDataType type) return "nanovdb_float"; case IMAGE_DATA_TYPE_NANOVDB_FLOAT3: return "nanovdb_float3"; + case IMAGE_DATA_TYPE_NANOVDB_FPN: + return "nanovdb_fpn"; + case IMAGE_DATA_TYPE_NANOVDB_FP16: + return "nanovdb_fp16"; case IMAGE_DATA_NUM_TYPES: assert(!"System enumerator type, should never be used"); return ""; @@ -378,7 +382,9 @@ void ImageManager::load_image_metadata(Image *img) metadata.detect_colorspace(); assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT || - metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3)); + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 || + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FPN || + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FP16)); img->need_metadata = false; } @@ -796,7 +802,8 @@ void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Pro } } #ifdef WITH_NANOVDB - else if (type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + else if (type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3 || + type == IMAGE_DATA_TYPE_NANOVDB_FPN || type == IMAGE_DATA_TYPE_NANOVDB_FP16) { thread_scoped_lock device_lock(device_mutex); void *pixels = img->mem->alloc(img->metadata.byte_size, 0); diff --git a/intern/cycles/scene/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp index 3f825afbe90..1b7f8f49696 100644 --- a/intern/cycles/scene/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -199,6 +199,8 @@ bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata, break; case IMAGE_DATA_TYPE_NANOVDB_FLOAT: case IMAGE_DATA_TYPE_NANOVDB_FLOAT3: + case IMAGE_DATA_TYPE_NANOVDB_FPN: + case IMAGE_DATA_TYPE_NANOVDB_FP16: case IMAGE_DATA_NUM_TYPES: break; } diff --git a/intern/cycles/scene/image_vdb.cpp b/intern/cycles/scene/image_vdb.cpp index b6f0911fa2c..d0b41a239df 100644 --- a/intern/cycles/scene/image_vdb.cpp +++ b/intern/cycles/scene/image_vdb.cpp @@ -44,14 +44,30 @@ struct ToDenseOp { # ifdef WITH_NANOVDB struct ToNanoOp { nanovdb::GridHandle<> nanogrid; + int precision; template<typename GridType, typename FloatGridType, typename FloatDataType, int channels> bool operator()(const openvdb::GridBase::ConstPtr &grid) { if constexpr (!std::is_same_v<GridType, openvdb::MaskGrid>) { try { - nanogrid = nanovdb::openToNanoVDB( - FloatGridType(*openvdb::gridConstPtrCast<GridType>(grid))); + FloatGridType floatgrid(*openvdb::gridConstPtrCast<GridType>(grid)); + if constexpr (std::is_same_v<FloatGridType, openvdb::FloatGrid>) { + if (precision == 0) { + nanogrid = nanovdb::openToNanoVDB<nanovdb::HostBuffer, + typename FloatGridType::TreeType, + nanovdb::FpN>(floatgrid); + return true; + } + else if (precision == 16) { + nanogrid = nanovdb::openToNanoVDB<nanovdb::HostBuffer, + typename FloatGridType::TreeType, + nanovdb::Fp16>(floatgrid); + return true; + } + } + + nanogrid = nanovdb::openToNanoVDB(floatgrid); } catch (const std::exception &e) { VLOG(1) << "Error converting OpenVDB to NanoVDB grid: " << e.what(); @@ -102,6 +118,7 @@ bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMet openvdb::tools::pruneInactive(pruned_grid.tree()); nanogrid = nanovdb::openToNanoVDB(pruned_grid);*/ ToNanoOp op; + op.precision = precision; if (!openvdb::grid_type_operation(grid, op)) { return false; } @@ -124,7 +141,15 @@ bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMet if (nanogrid) { metadata.byte_size = nanogrid.size(); if (metadata.channels == 1) { - metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT; + if (precision == 0) { + metadata.type = IMAGE_DATA_TYPE_NANOVDB_FPN; + } + else if (precision == 16) { + metadata.type = IMAGE_DATA_TYPE_NANOVDB_FP16; + } + else { + metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT; + } } else { metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3; diff --git a/intern/cycles/scene/image_vdb.h b/intern/cycles/scene/image_vdb.h index a5fd51915ef..ea5f6b0b3d9 100644 --- a/intern/cycles/scene/image_vdb.h +++ b/intern/cycles/scene/image_vdb.h @@ -51,6 +51,7 @@ class VDBImageLoader : public ImageLoader { #endif #ifdef WITH_NANOVDB nanovdb::GridHandle<> nanogrid; + int precision = 0; #endif }; diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp index 8015be6393b..ddd89a16640 100644 --- a/intern/cycles/scene/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -327,9 +327,11 @@ float Object::compute_volume_step_size() const /* Auto detect step size. */ float3 size = one_float3(); #ifdef WITH_NANOVDB - /* Dimensions were not applied to image transform with NanOVDB (see image_vdb.cpp) */ + /* Dimensions were not applied to image transform with NanoVDB (see image_vdb.cpp) */ if (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3 && + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FPN && + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FP16) #endif size /= make_float3(metadata.width, metadata.height, metadata.depth); diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 03c152928d5..3b58556f601 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -5882,7 +5882,7 @@ void BlackbodyNode::constant_fold(const ConstantFolder &folder) if (folder.all_inputs_constant()) { const float3 rgb_rec709 = svm_math_blackbody_color_rec709(temperature); const float3 rgb = folder.scene->shader_manager->rec709_to_scene_linear(rgb_rec709); - folder.make_constant(rgb); + folder.make_constant(max(rgb, zero_float3())); } } diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 143628f1e30..dac36ab0135 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -946,7 +946,7 @@ TEST_F(RenderGraph, constant_fold_bright_contrast) TEST_F(RenderGraph, constant_fold_blackbody) { EXPECT_ANY_MESSAGE(log); - CORRECT_INFO_MESSAGE(log, "Folding Blackbody::Color to constant (3.94163, 0.226523, 0)."); + CORRECT_INFO_MESSAGE(log, "Folding Blackbody::Color to constant (3.96553, 0.227897, 0)."); builder .add_node(ShaderNodeBuilder<BlackbodyNode>(graph, "Blackbody").set("Temperature", 1200.0f)) diff --git a/intern/cycles/util/texture.h b/intern/cycles/util/texture.h index e8bb058a3c9..90e842933c2 100644 --- a/intern/cycles/util/texture.h +++ b/intern/cycles/util/texture.h @@ -37,6 +37,8 @@ typedef enum ImageDataType { IMAGE_DATA_TYPE_USHORT = 7, IMAGE_DATA_TYPE_NANOVDB_FLOAT = 8, IMAGE_DATA_TYPE_NANOVDB_FLOAT3 = 9, + IMAGE_DATA_TYPE_NANOVDB_FPN = 10, + IMAGE_DATA_TYPE_NANOVDB_FP16 = 11, IMAGE_DATA_NUM_TYPES } ImageDataType; diff --git a/intern/ghost/intern/GHOST_Buttons.cpp b/intern/ghost/intern/GHOST_Buttons.cpp index c948c7beadb..3367d256325 100644 --- a/intern/ghost/intern/GHOST_Buttons.cpp +++ b/intern/ghost/intern/GHOST_Buttons.cpp @@ -21,6 +21,14 @@ bool GHOST_Buttons::get(GHOST_TButtonMask mask) const return m_ButtonMiddle; case GHOST_kButtonMaskRight: return m_ButtonRight; + case GHOST_kButtonMaskButton4: + return m_Button4; + case GHOST_kButtonMaskButton5: + return m_Button5; + case GHOST_kButtonMaskButton6: + return m_Button6; + case GHOST_kButtonMaskButton7: + return m_Button7; default: return false; } @@ -38,6 +46,18 @@ void GHOST_Buttons::set(GHOST_TButtonMask mask, bool down) case GHOST_kButtonMaskRight: m_ButtonRight = down; break; + case GHOST_kButtonMaskButton4: + m_Button4 = down; + break; + case GHOST_kButtonMaskButton5: + m_Button5 = down; + break; + case GHOST_kButtonMaskButton6: + m_Button6 = down; + break; + case GHOST_kButtonMaskButton7: + m_Button7 = down; + break; default: break; } @@ -48,6 +68,10 @@ void GHOST_Buttons::clear() m_ButtonLeft = false; m_ButtonMiddle = false; m_ButtonRight = false; + m_Button4 = false; + m_Button5 = false; + m_Button6 = false; + m_Button7 = false; } GHOST_Buttons::~GHOST_Buttons() diff --git a/intern/ghost/intern/GHOST_Buttons.h b/intern/ghost/intern/GHOST_Buttons.h index 17f25f1e082..72cb17a3322 100644 --- a/intern/ghost/intern/GHOST_Buttons.h +++ b/intern/ghost/intern/GHOST_Buttons.h @@ -44,4 +44,8 @@ struct GHOST_Buttons { uint8_t m_ButtonLeft : 1; uint8_t m_ButtonMiddle : 1; uint8_t m_ButtonRight : 1; + uint8_t m_Button4 : 1; + uint8_t m_Button5 : 1; + uint8_t m_Button6 : 1; + uint8_t m_Button7 : 1; }; diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp index fa9e2a8f360..7417358e9ae 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.cpp +++ b/intern/ghost/intern/GHOST_ContextWGL.cpp @@ -136,10 +136,10 @@ static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd, PIXELFORMATDESCRIPTOR /* cull unusable pixel formats */ /* if no formats can be found, can we determine why it was rejected? */ if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL) || !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || - !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */ + !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this. */ !(pfd.iPixelType == PFD_TYPE_RGBA) || - (pfd.cColorBits > 32) || /* 64 bit formats disable aero */ - (pfd.dwFlags & PFD_GENERIC_FORMAT)) /* no software renderers */ + (pfd.cColorBits > 32) || /* 64 bit formats disable AERO. */ + (pfd.dwFlags & PFD_GENERIC_FORMAT)) /* No software renderers. */ { return 0; } diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index d088b6717f9..b013127d1f3 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -125,7 +125,7 @@ uint8_t GHOST_SystemSDL::getNumDisplays() const return SDL_GetNumVideoDisplays(); } -GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSettings) +GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings /*glSettings*/) { GHOST_Context *context = new GHOST_ContextSDL(0, NULL, @@ -732,12 +732,12 @@ GHOST_TSuccess GHOST_SystemSDL::getButtons(GHOST_Buttons &buttons) const return GHOST_kSuccess; } -char *GHOST_SystemSDL::getClipboard(bool selection) const +char *GHOST_SystemSDL::getClipboard(bool /*selection*/) const { return (char *)SDL_GetClipboardText(); } -void GHOST_SystemSDL::putClipboard(const char *buffer, bool selection) const +void GHOST_SystemSDL::putClipboard(const char *buffer, bool /*selection*/) const { SDL_SetClipboardText(buffer); } diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h index 77707924675..aefea5eda34 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.h +++ b/intern/ghost/intern/GHOST_SystemSDL.h @@ -33,7 +33,7 @@ class GHOST_SystemSDL : public GHOST_System { bool processEvents(bool waitForEvent); - int setConsoleWindowState(GHOST_TConsoleWindowState action) + int setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) { return 0; } diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index dae3d578fa0..24d3f525c97 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -38,7 +38,8 @@ #include <cstring> -/* selected input event code defines from 'linux/input-event-codes.h' +/** + * Selected input event code defines from `linux/input-event-codes.h` * We include some of the button input event codes here, since the header is * only available in more recent kernel versions. The event codes are used to * to differentiate from which mouse button an event comes from. @@ -46,6 +47,11 @@ #define BTN_LEFT 0x110 #define BTN_RIGHT 0x111 #define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +// #define BTN_TASK 0x117 /* UNUSED. */ struct buffer_t { void *data; @@ -974,6 +980,18 @@ static void pointer_button(void *data, case BTN_RIGHT: ebutton = GHOST_kButtonMaskRight; break; + case BTN_SIDE: + ebutton = GHOST_kButtonMaskButton4; + break; + case BTN_EXTRA: + ebutton = GHOST_kButtonMaskButton5; + break; + case BTN_FORWARD: + ebutton = GHOST_kButtonMaskButton6; + break; + case BTN_BACK: + ebutton = GHOST_kButtonMaskButton7; + break; } input->data_source->source_serial = serial; @@ -1902,6 +1920,8 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) } GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, + const GHOST_TGrabCursorMode mode_current, + wl_surface *surface) { /* ignore, if the required protocols are not supported */ @@ -1915,36 +1935,37 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo input_t *input = d->inputs[0]; - switch (mode) { - case GHOST_kGrabDisable: - if (input->relative_pointer) { - zwp_relative_pointer_v1_destroy(input->relative_pointer); - input->relative_pointer = nullptr; - } - if (input->locked_pointer) { - zwp_locked_pointer_v1_destroy(input->locked_pointer); - input->locked_pointer = nullptr; - } - break; - - case GHOST_kGrabNormal: - break; - case GHOST_kGrabWrap: - input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( - d->relative_pointer_manager, input->pointer); - zwp_relative_pointer_v1_add_listener( - input->relative_pointer, &relative_pointer_listener, input); - input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( - d->pointer_constraints, - surface, - input->pointer, - nullptr, - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); - break; + if (mode != GHOST_kGrabDisable) { + /* TODO(@campbellbarton): Support #GHOST_kGrabWrap, + * where the cursor moves but is constrained to a region. */ + input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( + d->relative_pointer_manager, input->pointer); + zwp_relative_pointer_v1_add_listener( + input->relative_pointer, &relative_pointer_listener, input); + input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( + d->pointer_constraints, + surface, + input->pointer, + nullptr, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + + if (mode == GHOST_kGrabHide) { + setCursorVisibility(false); + } + } + else { + if (input->relative_pointer) { + zwp_relative_pointer_v1_destroy(input->relative_pointer); + input->relative_pointer = nullptr; + } + if (input->locked_pointer) { + zwp_locked_pointer_v1_destroy(input->locked_pointer); + input->locked_pointer = nullptr; + } - case GHOST_kGrabHide: + if (mode_current == GHOST_kGrabHide) { setCursorVisibility(false); - break; + } } return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 1f664915ad3..01b47358618 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -103,7 +103,9 @@ class GHOST_SystemWayland : public GHOST_System { GHOST_TSuccess setCursorVisibility(bool visible); - GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode, wl_surface *surface); + GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode, + const GHOST_TGrabCursorMode mode_current, + wl_surface *surface); private: struct display_t *d; diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index bbce6fdfdb5..54d38e1c0f3 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1117,7 +1117,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #else /* In keyboards like Latin ones, * numbers needs a 'Shift' to be accessed but key_sym - * is unmodified (or anyone swapping the keys with xmodmap). + * is unmodified (or anyone swapping the keys with `xmodmap`). * * Here we look at the 'Shifted' version of the key. * If it is a number, then we take it instead of the normal key. @@ -1612,6 +1612,8 @@ GHOST_TSuccess GHOST_SystemX11::getButtons(GHOST_Buttons &buttons) const buttons.set(GHOST_kButtonMaskLeft, (mask_return & Button1Mask) != 0); buttons.set(GHOST_kButtonMaskMiddle, (mask_return & Button2Mask) != 0); buttons.set(GHOST_kButtonMaskRight, (mask_return & Button3Mask) != 0); + buttons.set(GHOST_kButtonMaskButton4, (mask_return & Button4Mask) != 0); + buttons.set(GHOST_kButtonMaskButton5, (mask_return & Button5Mask) != 0); } else { return GHOST_kFailure; diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp index 7a9ff348b0a..168949771c9 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.cpp +++ b/intern/ghost/intern/GHOST_WindowSDL.cpp @@ -22,7 +22,7 @@ GHOST_WindowSDL::GHOST_WindowSDL(GHOST_SystemSDL *system, GHOST_TDrawingContextType type, const bool stereoVisual, const bool exclusive, - const GHOST_IWindow *parentWindow) + const GHOST_IWindow * /*parentWindow*/) : GHOST_Window(width, height, state, stereoVisual, exclusive), m_system(system), m_valid_setup(false), @@ -581,7 +581,7 @@ static SDL_Cursor *getStandardCursorShape(GHOST_TStandardCursor shape) return sdl_std_cursor_array[(int)shape]; } -GHOST_TSuccess GHOST_WindowSDL::setWindowCursorGrab(GHOST_TGrabCursorMode mode) +GHOST_TSuccess GHOST_WindowSDL::setWindowCursorGrab(GHOST_TGrabCursorMode /*mode*/) { return GHOST_kSuccess; } @@ -602,15 +602,20 @@ GHOST_TSuccess GHOST_WindowSDL::hasCursorShape(GHOST_TStandardCursor shape) return (getStandardCursorShape(shape)) ? GHOST_kSuccess : GHOST_kFailure; } -GHOST_TSuccess GHOST_WindowSDL::setWindowCustomCursorShape( - uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor) +GHOST_TSuccess GHOST_WindowSDL::setWindowCustomCursorShape(uint8_t *bitmap, + uint8_t *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool /*canInvertColor*/) { if (m_sdl_custom_cursor) { SDL_FreeCursor(m_sdl_custom_cursor); } m_sdl_custom_cursor = sdl_ghost_CreateCursor( - (const Uint8 *)bitmap, (const Uint8 *)mask, sizex, sizex, hotX, hotY); + (const Uint8 *)bitmap, (const Uint8 *)mask, sizex, sizey, hotX, hotY); SDL_SetCursor(m_sdl_custom_cursor); return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_WindowSDL.h b/intern/ghost/intern/GHOST_WindowSDL.h index cdea7e0d0b6..5805febab65 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.h +++ b/intern/ghost/intern/GHOST_WindowSDL.h @@ -109,7 +109,7 @@ class GHOST_WindowSDL : public GHOST_Window { GHOST_TWindowState getState() const; - GHOST_TSuccess setOrder(GHOST_TWindowOrder order) + GHOST_TSuccess setOrder(GHOST_TWindowOrder /*order*/) { // TODO return GHOST_kSuccess; diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 7ae06623c91..7f2a2c992d9 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -320,7 +320,7 @@ int &GHOST_WindowWayland::scale() GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { - return m_system->setCursorGrab(mode, w->surface); + return m_system->setCursorGrab(mode, m_cursorGrab, w->surface); } GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc index d78b34d3c92..8fc0ccd7918 100644 --- a/intern/opencolorio/fallback_impl.cc +++ b/intern/opencolorio/fallback_impl.cc @@ -241,10 +241,11 @@ void FallbackImpl::configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr * /*config*/, rgb[2] = 0.0722f; } -void FallbackImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr * /*config*/, float xyz_to_rgb[3][3]) +void FallbackImpl::configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr * /*config*/, + float xyz_to_scene_linear[3][3]) { /* Default to ITU-BT.709. */ - memcpy(xyz_to_rgb, OCIO_XYZ_TO_LINEAR_SRGB, sizeof(OCIO_XYZ_TO_LINEAR_SRGB)); + memcpy(xyz_to_scene_linear, OCIO_XYZ_TO_REC709, sizeof(OCIO_XYZ_TO_REC709)); } int FallbackImpl::configGetNumLooks(OCIO_ConstConfigRcPtr * /*config*/) diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc index 5e4c2a87a0b..b31f3794361 100644 --- a/intern/opencolorio/ocio_capi.cc +++ b/intern/opencolorio/ocio_capi.cc @@ -118,9 +118,9 @@ void OCIO_configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) impl->configGetDefaultLumaCoefs(config, rgb); } -void OCIO_configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config, float xyz_to_rgb[3][3]) +void OCIO_configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, float xyz_to_scene_linear[3][3]) { - impl->configGetXYZtoRGB(config, xyz_to_rgb); + impl->configGetXYZtoSceneLinear(config, xyz_to_scene_linear); } int OCIO_configGetNumLooks(OCIO_ConstConfigRcPtr *config) diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h index 9bd4ec374e2..97e6ff02779 100644 --- a/intern/opencolorio/ocio_capi.h +++ b/intern/opencolorio/ocio_capi.h @@ -31,10 +31,15 @@ OCIO_DECLARE_HANDLE(OCIO_ConstContextRcPtr); OCIO_DECLARE_HANDLE(OCIO_PackedImageDesc); OCIO_DECLARE_HANDLE(OCIO_ConstLookRcPtr); -/* Standard XYZ to linear sRGB transform, for fallback. */ -static const float OCIO_XYZ_TO_LINEAR_SRGB[3][3] = {{3.2404542f, -0.9692660f, 0.0556434f}, - {-1.5371385f, 1.8760108f, -0.2040259f}, - {-0.4985314f, 0.0415560f, 1.0572252f}}; +/* Standard XYZ (D65) to linear Rec.709 transform. */ +static const float OCIO_XYZ_TO_REC709[3][3] = {{3.2404542f, -0.9692660f, 0.0556434f}, + {-1.5371385f, 1.8760108f, -0.2040259f}, + {-0.4985314f, 0.0415560f, 1.0572252f}}; +/* Standard ACES to XYZ (D65) transform. + * Matches OpenColorIO builtin transform: UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */ +static const float OCIO_ACES_TO_XYZ[3][3] = {{0.938280f, 0.337369f, 0.001174f}, + {-0.004451f, 0.729522f, -0.003711f}, + {0.016628f, -0.066890f, 1.091595f}}; /* This structure is used to pass curve mapping settings from * blender's DNA structure stored in view transform settings @@ -130,7 +135,8 @@ const char *OCIO_configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, const char *view); void OCIO_configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb); -void OCIO_configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config, float xyz_to_rgb[3][3]); +void OCIO_configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, + float xyz_to_scene_linear[3][3]); int OCIO_configGetNumLooks(OCIO_ConstConfigRcPtr *config); const char *OCIO_configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index); diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc index 8d9c5dd2d49..7cf7fbefcd6 100644 --- a/intern/opencolorio/ocio_impl.cc +++ b/intern/opencolorio/ocio_impl.cc @@ -311,13 +311,14 @@ static bool to_scene_linear_matrix(ConstConfigRcPtr &config, return true; } -void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rgb[3][3]) +void OCIOImpl::configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config_, + float xyz_to_scene_linear[3][3]) { ConstConfigRcPtr config = (*(ConstConfigRcPtr *)config_); /* Default to ITU-BT.709 in case no appropriate transform found. * Note XYZ is defined here as having a D65 white point. */ - memcpy(xyz_to_rgb, OCIO_XYZ_TO_LINEAR_SRGB, sizeof(OCIO_XYZ_TO_LINEAR_SRGB)); + memcpy(xyz_to_scene_linear, OCIO_XYZ_TO_REC709, sizeof(OCIO_XYZ_TO_REC709)); /* Get from OpenColorO config if it has the required roles. */ if (!config->hasRole(ROLE_SCENE_LINEAR)) { @@ -326,22 +327,17 @@ void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rg if (config->hasRole("aces_interchange")) { /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */ - float aces_to_rgb[3][3]; - if (to_scene_linear_matrix(config, "aces_interchange", aces_to_rgb)) { - /* This is the OpenColorIO builtin transform: - * UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */ - const float ACES_AP0_to_xyz_D65[3][3] = {{0.938280f, 0.337369f, 0.001174f}, - {-0.004451f, 0.729522f, -0.003711f}, - {0.016628f, -0.066890f, 1.091595f}}; + float aces_to_scene_linear[3][3]; + if (to_scene_linear_matrix(config, "aces_interchange", aces_to_scene_linear)) { float xyz_to_aces[3][3]; - invert_m3_m3(xyz_to_aces, ACES_AP0_to_xyz_D65); + invert_m3_m3(xyz_to_aces, OCIO_ACES_TO_XYZ); - mul_m3_m3m3(xyz_to_rgb, aces_to_rgb, xyz_to_aces); + mul_m3_m3m3(xyz_to_scene_linear, aces_to_scene_linear, xyz_to_aces); } } else if (config->hasRole("XYZ")) { /* Custom role used before the standard existed. */ - to_scene_linear_matrix(config, "XYZ", xyz_to_rgb); + to_scene_linear_matrix(config, "XYZ", xyz_to_scene_linear); } } diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h index f8397c62e52..2c00eff6d6c 100644 --- a/intern/opencolorio/ocio_impl.h +++ b/intern/opencolorio/ocio_impl.h @@ -48,7 +48,8 @@ class IOCIOImpl { const char *view) = 0; virtual void configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) = 0; - virtual void configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config, float xyz_to_rgb[3][3]) = 0; + virtual void configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, + float xyz_to_scene_linear[3][3]) = 0; virtual int configGetNumLooks(OCIO_ConstConfigRcPtr *config) = 0; virtual const char *configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index) = 0; @@ -167,7 +168,7 @@ class FallbackImpl : public IOCIOImpl { const char *view); void configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb); - void configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config, float xyz_to_rgb[3][3]); + void configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, float xyz_to_scene_linear[3][3]); int configGetNumLooks(OCIO_ConstConfigRcPtr *config); const char *configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index); @@ -257,7 +258,7 @@ class OCIOImpl : public IOCIOImpl { const char *view); void configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb); - void configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config, float xyz_to_rgb[3][3]); + void configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, float xyz_to_scene_linear[3][3]); int configGetNumLooks(OCIO_ConstConfigRcPtr *config); const char *configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index); diff --git a/intern/opensubdiv/internal/evaluator/eval_output.h b/intern/opensubdiv/internal/evaluator/eval_output.h index bc5494bfe41..c0da108edca 100644 --- a/intern/opensubdiv/internal/evaluator/eval_output.h +++ b/intern/opensubdiv/internal/evaluator/eval_output.h @@ -103,6 +103,10 @@ class EvalOutputAPI::EvalOutput { { } + virtual void wrapSrcVertexDataBuffer(OpenSubdiv_Buffer * /*src_buffer*/) + { + } + virtual void fillFVarPatchArraysBuffer(const int /*face_varying_channel*/, OpenSubdiv_Buffer * /*patch_arrays_buffer*/) { @@ -122,6 +126,11 @@ class EvalOutputAPI::EvalOutput { OpenSubdiv_Buffer * /*src_buffer*/) { } + + virtual bool hasVertexData() const + { + return false; + } }; namespace { @@ -437,6 +446,11 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { return face_varying_evaluators_.size() != 0; } + bool hasVertexData() const override + { + return src_vertex_data_ != nullptr; + } + void refine() override { // Evaluate vertex positions. @@ -599,6 +613,11 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { return src_data_; } + SRC_VERTEX_BUFFER *getSrcVertexDataBuffer() const + { + return src_vertex_data_; + } + PATCH_TABLE *getPatchTable() const { return patch_table_; diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc b/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc index 566071d581b..274772b2c37 100644 --- a/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc +++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc @@ -86,6 +86,12 @@ void GpuEvalOutput::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO()); } +void GpuEvalOutput::wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer) +{ + GLVertexBuffer *vertex_buffer = getSrcVertexDataBuffer(); + src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO()); +} + void GpuEvalOutput::fillFVarPatchArraysBuffer(const int face_varying_channel, OpenSubdiv_Buffer *patch_arrays_buffer) { diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h index 2306a87b87c..8a4950dd3bc 100644 --- a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h +++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h @@ -52,6 +52,8 @@ class GpuEvalOutput : public VolatileEvalOutput<GLVertexBuffer, void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) override; + void wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer) override; + void fillFVarPatchArraysBuffer(const int face_varying_channel, OpenSubdiv_Buffer *patch_arrays_buffer) override; diff --git a/intern/opensubdiv/internal/evaluator/evaluator_capi.cc b/intern/opensubdiv/internal/evaluator/evaluator_capi.cc index 8a54ed653dc..5a3a2ff131c 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_capi.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_capi.cc @@ -191,6 +191,12 @@ void wrapSrcBuffer(struct OpenSubdiv_Evaluator *evaluator, struct OpenSubdiv_Buf evaluator->impl->eval_output->wrapSrcBuffer(src_buffer); } +void wrapSrcVertexDataBuffer(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *src_buffer) +{ + evaluator->impl->eval_output->wrapSrcVertexDataBuffer(src_buffer); +} + void fillFVarPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator, const int face_varying_channel, struct OpenSubdiv_Buffer *patch_array_buffer) @@ -220,6 +226,11 @@ void wrapFVarSrcBuffer(struct OpenSubdiv_Evaluator *evaluator, evaluator->impl->eval_output->wrapFVarSrcBuffer(face_varying_channel, src_buffer); } +bool hasVertexData(struct OpenSubdiv_Evaluator *evaluator) +{ + return evaluator->impl->eval_output->hasVertexData(); +} + void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator) { evaluator->setCoarsePositions = setCoarsePositions; @@ -246,11 +257,14 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator) evaluator->wrapPatchIndexBuffer = wrapPatchIndexBuffer; evaluator->wrapPatchParamBuffer = wrapPatchParamBuffer; evaluator->wrapSrcBuffer = wrapSrcBuffer; + evaluator->wrapSrcVertexDataBuffer = wrapSrcVertexDataBuffer; evaluator->fillFVarPatchArraysBuffer = fillFVarPatchArraysBuffer; evaluator->wrapFVarPatchIndexBuffer = wrapFVarPatchIndexBuffer; evaluator->wrapFVarPatchParamBuffer = wrapFVarPatchParamBuffer; evaluator->wrapFVarSrcBuffer = wrapFVarSrcBuffer; + + evaluator->hasVertexData = hasVertexData; } } // namespace diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc index bb9e6e7bd0d..a5273cad13a 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc @@ -383,6 +383,11 @@ void EvalOutputAPI::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) implementation_->wrapSrcBuffer(src_buffer); } +void EvalOutputAPI::wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer) +{ + implementation_->wrapSrcVertexDataBuffer(src_buffer); +} + void EvalOutputAPI::fillFVarPatchArraysBuffer(const int face_varying_channel, OpenSubdiv_Buffer *patch_arrays_buffer) { @@ -407,6 +412,11 @@ void EvalOutputAPI::wrapFVarSrcBuffer(const int face_varying_channel, implementation_->wrapFVarSrcBuffer(face_varying_channel, src_buffer); } +bool EvalOutputAPI::hasVertexData() const +{ + return implementation_->hasVertexData(); +} + } // namespace opensubdiv } // namespace blender diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.h b/intern/opensubdiv/internal/evaluator/evaluator_impl.h index 8ecfa4477be..df8ef70cc01 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.h +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.h @@ -163,6 +163,9 @@ class EvalOutputAPI { // Wrap the buffer used by OpenSubDiv for the source data with the given buffer. void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer); + // Wrap the buffer used by OpenSubDiv for the extra source data with the given buffer. + void wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer); + // Copy the patch arrays buffer used by OpenSubDiv for the face varying channel with the given // buffer. void fillFVarPatchArraysBuffer(const int face_varying_channel, @@ -181,6 +184,9 @@ class EvalOutputAPI { // Wrap thebuffer used by OpenSubDiv for the face varying channel with the given buffer. void wrapFVarSrcBuffer(const int face_varying_channel, OpenSubdiv_Buffer *src_buffer); + // Return true if source vertex data has been set. + bool hasVertexData() const; + protected: PatchMap *patch_map_; EvalOutput *implementation_; diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.h b/intern/opensubdiv/opensubdiv_evaluator_capi.h index 98e1db6e323..094244c4681 100644 --- a/intern/opensubdiv/opensubdiv_evaluator_capi.h +++ b/intern/opensubdiv/opensubdiv_evaluator_capi.h @@ -195,6 +195,10 @@ typedef struct OpenSubdiv_Evaluator { void (*wrapSrcBuffer)(struct OpenSubdiv_Evaluator *evaluator, struct OpenSubdiv_Buffer *src_buffer); + // Fill the given buffer with data from the evaluator's extra source buffer. + void (*wrapSrcVertexDataBuffer)(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *src_buffer); + // Fill the given buffer with data from the evaluator's face varying patch array buffer. void (*fillFVarPatchArraysBuffer)(struct OpenSubdiv_Evaluator *evaluator, const int face_varying_channel, @@ -215,6 +219,9 @@ typedef struct OpenSubdiv_Evaluator { const int face_varying_channel, struct OpenSubdiv_Buffer *src_buffer); + // Return true if the evaluator has source vertex data set. + bool (*hasVertexData)(struct OpenSubdiv_Evaluator *evaluator); + // Implementation of the evaluator. struct OpenSubdiv_EvaluatorImpl *impl; diff --git a/release/datafiles/blender_icons_geom.py b/release/datafiles/blender_icons_geom.py index 815bc6d49a4..b95baf3419e 100644 --- a/release/datafiles/blender_icons_geom.py +++ b/release/datafiles/blender_icons_geom.py @@ -45,6 +45,8 @@ import bpy # Generic functions +OBJECTS_TYPES_MESH_COMPATIBLE = {'CURVE', 'MESH'} + def area_tri_signed_2x_v2(v1, v2, v3): return (v1[0] - v2[0]) * (v2[1] - v3[1]) + (v1[1] - v2[1]) * (v3[0] - v2[0]) @@ -70,13 +72,14 @@ class TriMesh: @staticmethod def _tri_copy_from_object(ob): import bmesh - assert(ob.type == 'MESH') + assert(ob.type in OBJECTS_TYPES_MESH_COMPATIBLE) bm = bmesh.new() - bm.from_mesh(ob.data) + bm.from_mesh(ob.to_mesh()) bmesh.ops.triangulate(bm, faces=bm.faces) me = bpy.data.meshes.new(ob.name + ".copy") bm.to_mesh(me) bm.free() + ob.to_mesh_clear() return me @@ -118,7 +121,7 @@ def object_child_map(objects): def mesh_data_lists_from_mesh(me, material_colors): me_loops = me.loops[:] - me_loops_color = me.vertex_colors.active.data[:] + me_loops_color = me.attributes.active_color.data[:] me_verts = me.vertices[:] me_polys = me.polygons[:] @@ -164,16 +167,30 @@ def mesh_data_lists_from_mesh(me, material_colors): v1.co.xy[:], v2.co.xy[:], ), - # RGBA color. - tuple(( - [int(c * b * 255) for c, b in zip(cn.color, base_color)] - for cn in (c0, c1, c2) - )), + # RGBA color in sRGB color space. + ( + color_multiply_and_from_linear_to_srgb(base_color, c0), + color_multiply_and_from_linear_to_srgb(base_color, c1), + color_multiply_and_from_linear_to_srgb(base_color, c2), + ), )) i1 = i2 return tris_data +def color_multiply_and_from_linear_to_srgb(base_color, vertex_color): + """ + Return the RGBA color in sRGB and byte format (0-255). + + base_color and vertex_color are expected in linear space. + The final color is the product between the base color and the vertex color. + """ + import mathutils + color_linear = [c * b for c, b in zip(vertex_color.color, base_color)] + color_srgb = mathutils.Color(color_linear[:3]).from_scene_linear_to_srgb() + return tuple(round(c * 255) for c in (*color_srgb, color_linear[3])) + + def mesh_data_lists_from_objects(ob_parent, ob_children): tris_data = [] @@ -203,7 +220,7 @@ def write_mesh_to_py(fh, ob, ob_children): assert(axis_range <= 255) # -1..1 -> 0..255 f = (f + 1.0) * 0.5 - f = int(round(f * axis_range)) + f = round(f * axis_range) return min(max(f, 0), axis_range) def vert_as_byte_pair(v): @@ -300,6 +317,7 @@ def main(): args = parser.parse_args(argv) objects = [] + depsgraph = bpy.context.view_layer.depsgraph if args.group: group = bpy.data.collections.get(args.group) @@ -314,23 +332,25 @@ def main(): for ob in objects_source: # Skip non-mesh objects - if ob.type != 'MESH': + if ob.type not in OBJECTS_TYPES_MESH_COMPATIBLE: continue - name = ob.name + + ob_eval = ob.evaluated_get(depsgraph) + name = ob_eval.name # Skip copies of objects if name.rpartition(".")[2].isdigit(): continue - if not ob.data.vertex_colors: + if not ob_eval.data.attributes.active_color: print("Skipping:", name, "(no vertex colors)") continue - objects.append((name, ob)) + objects.append((name, ob_eval)) objects.sort(key=lambda a: a[0]) - objects_children = object_child_map(bpy.data.objects) + objects_children = object_child_map(depsgraph.objects) for name, ob in objects: if ob.parent: diff --git a/release/datafiles/icons/brush.gpencil_draw.erase.dat b/release/datafiles/icons/brush.gpencil_draw.erase.dat Binary files differindex 5b96035c9b8..d2faa6f2112 100644 --- a/release/datafiles/icons/brush.gpencil_draw.erase.dat +++ b/release/datafiles/icons/brush.gpencil_draw.erase.dat diff --git a/release/datafiles/icons/brush.paint_texture.multiply.dat b/release/datafiles/icons/brush.paint_texture.multiply.dat Binary files differindex 948993203be..75fa09032a8 100644 --- a/release/datafiles/icons/brush.paint_texture.multiply.dat +++ b/release/datafiles/icons/brush.paint_texture.multiply.dat diff --git a/release/datafiles/icons/brush.paint_vertex.replace.dat b/release/datafiles/icons/brush.paint_vertex.replace.dat Binary files differindex 676436548a7..a9a9d4f047f 100644 --- a/release/datafiles/icons/brush.paint_vertex.replace.dat +++ b/release/datafiles/icons/brush.paint_vertex.replace.dat diff --git a/release/datafiles/icons/brush.particle.comb.dat b/release/datafiles/icons/brush.particle.comb.dat Binary files differindex d6dd75a35d7..1cdbcbadcdc 100644 --- a/release/datafiles/icons/brush.particle.comb.dat +++ b/release/datafiles/icons/brush.particle.comb.dat diff --git a/release/datafiles/icons/brush.particle.cut.dat b/release/datafiles/icons/brush.particle.cut.dat Binary files differindex e7ef86e2fbc..0acc00660db 100644 --- a/release/datafiles/icons/brush.particle.cut.dat +++ b/release/datafiles/icons/brush.particle.cut.dat diff --git a/release/datafiles/icons/brush.particle.puff.dat b/release/datafiles/icons/brush.particle.puff.dat Binary files differindex db2bab46bfe..72b4851cc89 100644 --- a/release/datafiles/icons/brush.particle.puff.dat +++ b/release/datafiles/icons/brush.particle.puff.dat diff --git a/release/datafiles/icons/brush.particle.smooth.dat b/release/datafiles/icons/brush.particle.smooth.dat Binary files differindex 7deaa4ed082..ba4bd081f33 100644 --- a/release/datafiles/icons/brush.particle.smooth.dat +++ b/release/datafiles/icons/brush.particle.smooth.dat diff --git a/release/datafiles/icons/brush.sculpt.boundary.dat b/release/datafiles/icons/brush.sculpt.boundary.dat Binary files differindex 8d56baf2254..1b71d24d771 100644 --- a/release/datafiles/icons/brush.sculpt.boundary.dat +++ b/release/datafiles/icons/brush.sculpt.boundary.dat diff --git a/release/datafiles/icons/brush.sculpt.cloth.dat b/release/datafiles/icons/brush.sculpt.cloth.dat Binary files differindex 7e936167381..15ad0c7dc94 100644 --- a/release/datafiles/icons/brush.sculpt.cloth.dat +++ b/release/datafiles/icons/brush.sculpt.cloth.dat diff --git a/release/datafiles/icons/brush.sculpt.displacement_eraser.dat b/release/datafiles/icons/brush.sculpt.displacement_eraser.dat Binary files differindex e4637b9b12f..5c479fd59cb 100644 --- a/release/datafiles/icons/brush.sculpt.displacement_eraser.dat +++ b/release/datafiles/icons/brush.sculpt.displacement_eraser.dat diff --git a/release/datafiles/icons/brush.sculpt.displacement_smear.dat b/release/datafiles/icons/brush.sculpt.displacement_smear.dat Binary files differindex 9e4df45b2d2..ed84702cbd3 100644 --- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat +++ b/release/datafiles/icons/brush.sculpt.displacement_smear.dat diff --git a/release/datafiles/icons/brush.sculpt.draw_face_sets.dat b/release/datafiles/icons/brush.sculpt.draw_face_sets.dat Binary files differindex a234aa7af2c..ade40de029d 100644 --- a/release/datafiles/icons/brush.sculpt.draw_face_sets.dat +++ b/release/datafiles/icons/brush.sculpt.draw_face_sets.dat diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat Binary files differindex 9bea1b02894..cf606324b07 100644 --- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat +++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat diff --git a/release/datafiles/icons/brush.sculpt.elastic_deform.dat b/release/datafiles/icons/brush.sculpt.elastic_deform.dat Binary files differindex 0b12d717d3a..db57ef2fac5 100644 --- a/release/datafiles/icons/brush.sculpt.elastic_deform.dat +++ b/release/datafiles/icons/brush.sculpt.elastic_deform.dat diff --git a/release/datafiles/icons/brush.sculpt.fill.dat b/release/datafiles/icons/brush.sculpt.fill.dat Binary files differindex b2898919bd4..08fe0711d33 100644 --- a/release/datafiles/icons/brush.sculpt.fill.dat +++ b/release/datafiles/icons/brush.sculpt.fill.dat diff --git a/release/datafiles/icons/brush.sculpt.flatten.dat b/release/datafiles/icons/brush.sculpt.flatten.dat Binary files differindex 25b5f0cf8a9..a4439d75eed 100644 --- a/release/datafiles/icons/brush.sculpt.flatten.dat +++ b/release/datafiles/icons/brush.sculpt.flatten.dat diff --git a/release/datafiles/icons/brush.sculpt.grab.dat b/release/datafiles/icons/brush.sculpt.grab.dat Binary files differindex 0b61977e792..819c9263b45 100644 --- a/release/datafiles/icons/brush.sculpt.grab.dat +++ b/release/datafiles/icons/brush.sculpt.grab.dat diff --git a/release/datafiles/icons/brush.sculpt.layer.dat b/release/datafiles/icons/brush.sculpt.layer.dat Binary files differindex 1031d95332a..337362185e0 100644 --- a/release/datafiles/icons/brush.sculpt.layer.dat +++ b/release/datafiles/icons/brush.sculpt.layer.dat diff --git a/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat b/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat Binary files differindex 6e17f520282..ea64b2d9e45 100644 --- a/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat +++ b/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat diff --git a/release/datafiles/icons/brush.sculpt.nudge.dat b/release/datafiles/icons/brush.sculpt.nudge.dat Binary files differindex e10157e9cd0..f5a93ede065 100644 --- a/release/datafiles/icons/brush.sculpt.nudge.dat +++ b/release/datafiles/icons/brush.sculpt.nudge.dat diff --git a/release/datafiles/icons/brush.sculpt.pinch.dat b/release/datafiles/icons/brush.sculpt.pinch.dat Binary files differindex abdb62dcfc8..7dbbdef3bcc 100644 --- a/release/datafiles/icons/brush.sculpt.pinch.dat +++ b/release/datafiles/icons/brush.sculpt.pinch.dat diff --git a/release/datafiles/icons/brush.sculpt.pose.dat b/release/datafiles/icons/brush.sculpt.pose.dat Binary files differindex 6183583ea27..6bd7764bf23 100644 --- a/release/datafiles/icons/brush.sculpt.pose.dat +++ b/release/datafiles/icons/brush.sculpt.pose.dat diff --git a/release/datafiles/icons/brush.sculpt.rotate.dat b/release/datafiles/icons/brush.sculpt.rotate.dat Binary files differindex 8d1723a8c71..b7b8f083bde 100644 --- a/release/datafiles/icons/brush.sculpt.rotate.dat +++ b/release/datafiles/icons/brush.sculpt.rotate.dat diff --git a/release/datafiles/icons/brush.sculpt.scrape.dat b/release/datafiles/icons/brush.sculpt.scrape.dat Binary files differindex 9b37a9876a1..afb5a46a3bd 100644 --- a/release/datafiles/icons/brush.sculpt.scrape.dat +++ b/release/datafiles/icons/brush.sculpt.scrape.dat diff --git a/release/datafiles/icons/brush.sculpt.smooth.dat b/release/datafiles/icons/brush.sculpt.smooth.dat Binary files differindex 36d8098ad26..c42049cb51a 100644 --- a/release/datafiles/icons/brush.sculpt.smooth.dat +++ b/release/datafiles/icons/brush.sculpt.smooth.dat diff --git a/release/datafiles/icons/brush.sculpt.snake_hook.dat b/release/datafiles/icons/brush.sculpt.snake_hook.dat Binary files differindex 20300c1d97c..ce47b7141cf 100644 --- a/release/datafiles/icons/brush.sculpt.snake_hook.dat +++ b/release/datafiles/icons/brush.sculpt.snake_hook.dat diff --git a/release/datafiles/icons/brush.sculpt.thumb.dat b/release/datafiles/icons/brush.sculpt.thumb.dat Binary files differindex 9da33eccd98..f54a141eca7 100644 --- a/release/datafiles/icons/brush.sculpt.thumb.dat +++ b/release/datafiles/icons/brush.sculpt.thumb.dat diff --git a/release/datafiles/icons/brush.sculpt.topology.dat b/release/datafiles/icons/brush.sculpt.topology.dat Binary files differindex 0d455dce556..ec3699a7c2d 100644 --- a/release/datafiles/icons/brush.sculpt.topology.dat +++ b/release/datafiles/icons/brush.sculpt.topology.dat diff --git a/release/datafiles/icons/brush.uv_sculpt.relax.dat b/release/datafiles/icons/brush.uv_sculpt.relax.dat Binary files differindex 3a30ac72cb6..1df2828c669 100644 --- a/release/datafiles/icons/brush.uv_sculpt.relax.dat +++ b/release/datafiles/icons/brush.uv_sculpt.relax.dat diff --git a/release/datafiles/icons/ops.armature.bone.roll.dat b/release/datafiles/icons/ops.armature.bone.roll.dat Binary files differindex bb5158c43c9..3945b2fdffd 100644 --- a/release/datafiles/icons/ops.armature.bone.roll.dat +++ b/release/datafiles/icons/ops.armature.bone.roll.dat diff --git a/release/datafiles/icons/ops.curve.pen.dat b/release/datafiles/icons/ops.curve.pen.dat Binary files differindex 1007f7ea604..8031fdd8606 100644 --- a/release/datafiles/icons/ops.curve.pen.dat +++ b/release/datafiles/icons/ops.curve.pen.dat diff --git a/release/datafiles/icons/ops.curve.radius.dat b/release/datafiles/icons/ops.curve.radius.dat Binary files differindex 1c887bc11e7..e7b7913fb22 100644 --- a/release/datafiles/icons/ops.curve.radius.dat +++ b/release/datafiles/icons/ops.curve.radius.dat diff --git a/release/datafiles/icons/ops.curve.vertex_random.dat b/release/datafiles/icons/ops.curve.vertex_random.dat Binary files differindex ced246f4eae..75f1973c7a3 100644 --- a/release/datafiles/icons/ops.curve.vertex_random.dat +++ b/release/datafiles/icons/ops.curve.vertex_random.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_comb.dat b/release/datafiles/icons/ops.curves.sculpt_comb.dat Binary files differindex d6dd75a35d7..1cdbcbadcdc 100644 --- a/release/datafiles/icons/ops.curves.sculpt_comb.dat +++ b/release/datafiles/icons/ops.curves.sculpt_comb.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_cut.dat b/release/datafiles/icons/ops.curves.sculpt_cut.dat Binary files differindex e7ef86e2fbc..0acc00660db 100644 --- a/release/datafiles/icons/ops.curves.sculpt_cut.dat +++ b/release/datafiles/icons/ops.curves.sculpt_cut.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_delete.dat b/release/datafiles/icons/ops.curves.sculpt_delete.dat Binary files differindex 896d472e017..bd76aef9a81 100644 --- a/release/datafiles/icons/ops.curves.sculpt_delete.dat +++ b/release/datafiles/icons/ops.curves.sculpt_delete.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_pinch.dat b/release/datafiles/icons/ops.curves.sculpt_pinch.dat Binary files differnew file mode 100644 index 00000000000..49f40efc931 --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_pinch.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_puff.dat b/release/datafiles/icons/ops.curves.sculpt_puff.dat Binary files differindex db2bab46bfe..72b4851cc89 100644 --- a/release/datafiles/icons/ops.curves.sculpt_puff.dat +++ b/release/datafiles/icons/ops.curves.sculpt_puff.dat diff --git a/release/datafiles/icons/ops.generic.select_box.dat b/release/datafiles/icons/ops.generic.select_box.dat Binary files differindex da435ab3925..609c2f76927 100644 --- a/release/datafiles/icons/ops.generic.select_box.dat +++ b/release/datafiles/icons/ops.generic.select_box.dat diff --git a/release/datafiles/icons/ops.generic.select_circle.dat b/release/datafiles/icons/ops.generic.select_circle.dat Binary files differindex 83e1deb119d..d9fcda69be1 100644 --- a/release/datafiles/icons/ops.generic.select_circle.dat +++ b/release/datafiles/icons/ops.generic.select_circle.dat diff --git a/release/datafiles/icons/ops.generic.select_lasso.dat b/release/datafiles/icons/ops.generic.select_lasso.dat Binary files differindex 54994c48a3d..f8ec3d4d199 100644 --- a/release/datafiles/icons/ops.generic.select_lasso.dat +++ b/release/datafiles/icons/ops.generic.select_lasso.dat diff --git a/release/datafiles/icons/ops.generic.select_paint.dat b/release/datafiles/icons/ops.generic.select_paint.dat Binary files differindex 26d76d4548f..745cd073967 100644 --- a/release/datafiles/icons/ops.generic.select_paint.dat +++ b/release/datafiles/icons/ops.generic.select_paint.dat diff --git a/release/datafiles/icons/ops.gpencil.draw.eraser.dat b/release/datafiles/icons/ops.gpencil.draw.eraser.dat Binary files differindex 44f65c4581d..d1be26fa43f 100644 --- a/release/datafiles/icons/ops.gpencil.draw.eraser.dat +++ b/release/datafiles/icons/ops.gpencil.draw.eraser.dat diff --git a/release/datafiles/icons/ops.gpencil.edit_bend.dat b/release/datafiles/icons/ops.gpencil.edit_bend.dat Binary files differindex 81fcb7cbf27..ebbddf6a455 100644 --- a/release/datafiles/icons/ops.gpencil.edit_bend.dat +++ b/release/datafiles/icons/ops.gpencil.edit_bend.dat diff --git a/release/datafiles/icons/ops.gpencil.edit_shear.dat b/release/datafiles/icons/ops.gpencil.edit_shear.dat Binary files differindex e591b102499..6f1f53d0ea0 100644 --- a/release/datafiles/icons/ops.gpencil.edit_shear.dat +++ b/release/datafiles/icons/ops.gpencil.edit_shear.dat diff --git a/release/datafiles/icons/ops.gpencil.edit_to_sphere.dat b/release/datafiles/icons/ops.gpencil.edit_to_sphere.dat Binary files differindex 485d0a80c79..32bbb64fdba 100644 --- a/release/datafiles/icons/ops.gpencil.edit_to_sphere.dat +++ b/release/datafiles/icons/ops.gpencil.edit_to_sphere.dat diff --git a/release/datafiles/icons/ops.gpencil.radius.dat b/release/datafiles/icons/ops.gpencil.radius.dat Binary files differindex 1c887bc11e7..e7b7913fb22 100644 --- a/release/datafiles/icons/ops.gpencil.radius.dat +++ b/release/datafiles/icons/ops.gpencil.radius.dat diff --git a/release/datafiles/icons/ops.gpencil.stroke_cutter.dat b/release/datafiles/icons/ops.gpencil.stroke_cutter.dat Binary files differindex 31a130a23f5..d69e61cf74f 100644 --- a/release/datafiles/icons/ops.gpencil.stroke_cutter.dat +++ b/release/datafiles/icons/ops.gpencil.stroke_cutter.dat diff --git a/release/datafiles/icons/ops.gpencil.transform_fill.dat b/release/datafiles/icons/ops.gpencil.transform_fill.dat Binary files differindex a364882d33f..8983a6c5eaf 100644 --- a/release/datafiles/icons/ops.gpencil.transform_fill.dat +++ b/release/datafiles/icons/ops.gpencil.transform_fill.dat diff --git a/release/datafiles/icons/ops.mesh.rip.dat b/release/datafiles/icons/ops.mesh.rip.dat Binary files differindex fb1000c66aa..d55520ab18f 100644 --- a/release/datafiles/icons/ops.mesh.rip.dat +++ b/release/datafiles/icons/ops.mesh.rip.dat diff --git a/release/datafiles/icons/ops.mesh.rip_edge.dat b/release/datafiles/icons/ops.mesh.rip_edge.dat Binary files differindex e8b494e07c9..d60b969c714 100644 --- a/release/datafiles/icons/ops.mesh.rip_edge.dat +++ b/release/datafiles/icons/ops.mesh.rip_edge.dat diff --git a/release/datafiles/icons/ops.mesh.vertices_smooth.dat b/release/datafiles/icons/ops.mesh.vertices_smooth.dat Binary files differindex e460bbaeed8..aa1ea6d424e 100644 --- a/release/datafiles/icons/ops.mesh.vertices_smooth.dat +++ b/release/datafiles/icons/ops.mesh.vertices_smooth.dat diff --git a/release/datafiles/icons/ops.node.links_cut.dat b/release/datafiles/icons/ops.node.links_cut.dat Binary files differindex dbe79cb9661..2ca6f360cfc 100644 --- a/release/datafiles/icons/ops.node.links_cut.dat +++ b/release/datafiles/icons/ops.node.links_cut.dat diff --git a/release/datafiles/icons/ops.pose.push.dat b/release/datafiles/icons/ops.pose.push.dat Binary files differindex 9a56909dbac..58b838eb558 100644 --- a/release/datafiles/icons/ops.pose.push.dat +++ b/release/datafiles/icons/ops.pose.push.dat diff --git a/release/datafiles/icons/ops.pose.relax.dat b/release/datafiles/icons/ops.pose.relax.dat Binary files differindex e9849dcb374..32430f479b7 100644 --- a/release/datafiles/icons/ops.pose.relax.dat +++ b/release/datafiles/icons/ops.pose.relax.dat diff --git a/release/datafiles/icons/ops.sculpt.border_face_set.dat b/release/datafiles/icons/ops.sculpt.border_face_set.dat Binary files differindex a34bcd461eb..55343c5ba8f 100644 --- a/release/datafiles/icons/ops.sculpt.border_face_set.dat +++ b/release/datafiles/icons/ops.sculpt.border_face_set.dat diff --git a/release/datafiles/icons/ops.sculpt.box_trim.dat b/release/datafiles/icons/ops.sculpt.box_trim.dat Binary files differindex 558c2b162ac..96e98b97785 100644 --- a/release/datafiles/icons/ops.sculpt.box_trim.dat +++ b/release/datafiles/icons/ops.sculpt.box_trim.dat diff --git a/release/datafiles/icons/ops.sculpt.cloth_filter.dat b/release/datafiles/icons/ops.sculpt.cloth_filter.dat Binary files differindex dc20c8f0bfd..2dd3b983368 100644 --- a/release/datafiles/icons/ops.sculpt.cloth_filter.dat +++ b/release/datafiles/icons/ops.sculpt.cloth_filter.dat diff --git a/release/datafiles/icons/ops.sculpt.face_set_edit.dat b/release/datafiles/icons/ops.sculpt.face_set_edit.dat Binary files differindex 2e5ec79ef8c..ad83305374a 100644 --- a/release/datafiles/icons/ops.sculpt.face_set_edit.dat +++ b/release/datafiles/icons/ops.sculpt.face_set_edit.dat diff --git a/release/datafiles/icons/ops.sculpt.lasso_face_set.dat b/release/datafiles/icons/ops.sculpt.lasso_face_set.dat Binary files differindex 55c044a2ed1..a4d32f56c99 100644 --- a/release/datafiles/icons/ops.sculpt.lasso_face_set.dat +++ b/release/datafiles/icons/ops.sculpt.lasso_face_set.dat diff --git a/release/datafiles/icons/ops.sculpt.lasso_trim.dat b/release/datafiles/icons/ops.sculpt.lasso_trim.dat Binary files differindex 60c33fc9f2f..32e9c74e00b 100644 --- a/release/datafiles/icons/ops.sculpt.lasso_trim.dat +++ b/release/datafiles/icons/ops.sculpt.lasso_trim.dat diff --git a/release/datafiles/icons/ops.transform.bone_envelope.dat b/release/datafiles/icons/ops.transform.bone_envelope.dat Binary files differindex 2f684152e5f..26238814e9e 100644 --- a/release/datafiles/icons/ops.transform.bone_envelope.dat +++ b/release/datafiles/icons/ops.transform.bone_envelope.dat diff --git a/release/datafiles/icons/ops.transform.bone_size.dat b/release/datafiles/icons/ops.transform.bone_size.dat Binary files differindex d884056d76c..e00d14df608 100644 --- a/release/datafiles/icons/ops.transform.bone_size.dat +++ b/release/datafiles/icons/ops.transform.bone_size.dat diff --git a/release/datafiles/icons/ops.transform.edge_slide.dat b/release/datafiles/icons/ops.transform.edge_slide.dat Binary files differindex 7fb29564551..851b84915ef 100644 --- a/release/datafiles/icons/ops.transform.edge_slide.dat +++ b/release/datafiles/icons/ops.transform.edge_slide.dat diff --git a/release/datafiles/icons/ops.transform.push_pull.dat b/release/datafiles/icons/ops.transform.push_pull.dat Binary files differindex e9a74bfd6e2..51f01da16d2 100644 --- a/release/datafiles/icons/ops.transform.push_pull.dat +++ b/release/datafiles/icons/ops.transform.push_pull.dat diff --git a/release/datafiles/icons/ops.transform.resize.cage.dat b/release/datafiles/icons/ops.transform.resize.cage.dat Binary files differindex 8eabffad087..f4e94c7f804 100644 --- a/release/datafiles/icons/ops.transform.resize.cage.dat +++ b/release/datafiles/icons/ops.transform.resize.cage.dat diff --git a/release/datafiles/icons/ops.transform.resize.dat b/release/datafiles/icons/ops.transform.resize.dat Binary files differindex 9c26224da68..0d1b5f60e62 100644 --- a/release/datafiles/icons/ops.transform.resize.dat +++ b/release/datafiles/icons/ops.transform.resize.dat diff --git a/release/datafiles/icons/ops.transform.rotate.dat b/release/datafiles/icons/ops.transform.rotate.dat Binary files differindex f42bd25137e..0afb420aab7 100644 --- a/release/datafiles/icons/ops.transform.rotate.dat +++ b/release/datafiles/icons/ops.transform.rotate.dat diff --git a/release/datafiles/icons/ops.transform.shear.dat b/release/datafiles/icons/ops.transform.shear.dat Binary files differindex 92912e2cfb7..aeadc7e3701 100644 --- a/release/datafiles/icons/ops.transform.shear.dat +++ b/release/datafiles/icons/ops.transform.shear.dat diff --git a/release/datafiles/icons/ops.transform.shrink_fatten.dat b/release/datafiles/icons/ops.transform.shrink_fatten.dat Binary files differindex 8d1d4c130a4..5c683c94c96 100644 --- a/release/datafiles/icons/ops.transform.shrink_fatten.dat +++ b/release/datafiles/icons/ops.transform.shrink_fatten.dat diff --git a/release/datafiles/icons/ops.transform.tilt.dat b/release/datafiles/icons/ops.transform.tilt.dat Binary files differindex a0f1040e1cf..70316d37d81 100644 --- a/release/datafiles/icons/ops.transform.tilt.dat +++ b/release/datafiles/icons/ops.transform.tilt.dat diff --git a/release/datafiles/icons/ops.transform.tosphere.dat b/release/datafiles/icons/ops.transform.tosphere.dat Binary files differindex b55b4b1f283..5976ac5e224 100644 --- a/release/datafiles/icons/ops.transform.tosphere.dat +++ b/release/datafiles/icons/ops.transform.tosphere.dat diff --git a/release/datafiles/icons/ops.transform.vert_slide.dat b/release/datafiles/icons/ops.transform.vert_slide.dat Binary files differindex 26e8696e336..1f97030e99b 100644 --- a/release/datafiles/icons/ops.transform.vert_slide.dat +++ b/release/datafiles/icons/ops.transform.vert_slide.dat diff --git a/release/datafiles/icons/ops.transform.vertex_random.dat b/release/datafiles/icons/ops.transform.vertex_random.dat Binary files differindex 876d0649dbd..b094f0658e9 100644 --- a/release/datafiles/icons/ops.transform.vertex_random.dat +++ b/release/datafiles/icons/ops.transform.vertex_random.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject fb1eac2ec80c0adee69990a5386b74a5bd4ca00 +Subproject 647c85462d87c3a9d3a189d28d72d1bd93f4d4a diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 7025cd28ede25eb44208722f842e35b10325c6c +Subproject d936e4c01fa263a71a7d0665628ae621283b15e diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py index 687f3c95d0b..04120508df5 100644 --- a/release/scripts/modules/rna_info.py +++ b/release/scripts/modules/rna_info.py @@ -61,7 +61,8 @@ def range_str(val): def float_as_string(f): val_str = "%g" % f - if '.' not in val_str and '-' not in val_str: # value could be 1e-05 + # Ensure a `.0` suffix for whole numbers, excluding scientific notation such as `1e-05` or `1e+5`. + if '.' not in val_str and 'e' not in val_str: val_str += '.0' return val_str @@ -584,6 +585,16 @@ def BuildRNAInfo(): structs = [] def _bpy_types_iterator(): + # Don't report when these types are ignored. + suppress_warning = { + "bpy_func", + "bpy_prop", + "bpy_prop_array", + "bpy_prop_collection", + "bpy_struct", + "bpy_struct_meta_idprop", + } + names_unique = set() rna_type_list = [] for rna_type_name in dir(bpy.types): @@ -593,8 +604,13 @@ def BuildRNAInfo(): if rna_struct is not None: rna_type_list.append(rna_type) yield (rna_type_name, rna_struct) + elif rna_type_name.startswith("_"): + # Ignore "__dir__", "__getattr__" .. etc. + pass + elif rna_type_name in suppress_warning: + pass else: - print("Ignoring", rna_type_name) + print("rna_info.BuildRNAInfo(..): ignoring type", repr(rna_type_name)) # Now, there are some sub-classes in add-ons we also want to include. # Cycles for e.g. these are referenced from the Scene, but not part of diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 9c94029fa16..a4bac916946 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2031,13 +2031,9 @@ def km_node_editor(params): items.extend(_template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)) else: items.extend(_template_node_select( - type='RIGHTMOUSE', value=params.select_mouse_value, select_passthrough=False)) - items.extend([ - ("node.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, - {"properties": [("deselect_all", False)]}), - ("node.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, - {"properties": [("toggle", True)]}), - ]) + type='RIGHTMOUSE', value=params.select_mouse_value, select_passthrough=True)) + items.extend(_template_node_select( + type='LEFTMOUSE', value='PRESS', select_passthrough=True)) items.extend([ ("node.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, @@ -4795,7 +4791,7 @@ def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=Tru def _template_node_select(*, type, value, select_passthrough): items = [ ("node.select", {"type": type, "value": value}, - {"properties": [("deselect_all", True), ("select_passthrough", True)]}), + {"properties": [("deselect_all", True), ("select_passthrough", select_passthrough)]}), ("node.select", {"type": type, "value": value, "ctrl": True}, None), ("node.select", {"type": type, "value": value, "alt": True}, None), ("node.select", {"type": type, "value": value, "ctrl": True, "alt": True}, None), diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 0f063da40fb..37d7ef19a28 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -21,10 +21,68 @@ from bpy.props import ( ) from bpy.app.translations import pgettext_iface as iface_ + +def _rna_path_prop_search_for_context_impl(context, edit_text, unique_attrs): + # Use the same logic as auto-completing in the Python console to expand the data-path. + from bl_console_utils.autocomplete import intellisense + context_prefix = "context." + line = context_prefix + edit_text + cursor = len(line) + namespace = {"context": context} + comp_prefix, _, comp_options = intellisense.expand(line=line, cursor=len(line), namespace=namespace, private=False) + prefix = comp_prefix[len(context_prefix):] # Strip "context." + for attr in comp_options.split("\n"): + if attr.endswith(( + # Exclude function calls because they are generally not part of data-paths. + "(", ")", + # RNA properties for introspection, not useful to expand. + ".bl_rna", ".rna_type", + )): + continue + attr_full = prefix + attr.lstrip() + if attr_full in unique_attrs: + continue + unique_attrs.add(attr_full) + yield attr_full + + +def rna_path_prop_search_for_context(self, context, edit_text): + # NOTE(@campbellbarton): Limiting data-path expansion is rather arbitrary. + # It's possible for e.g. that someone would want to set a shortcut in the preferences or + # in other region types than those currently expanded. Unless there is a reasonable likelihood + # users might expand these space-type/region-type combinations - exclude them from this search. + # After all, this list is mainly intended as a hint, users are not prevented from constructing + # the data-paths themselves. + unique_attrs = set() + + for window in context.window_manager.windows: + for area in window.screen.areas: + # Users are very unlikely to be setting shortcuts in the preferences, skip this. + if area.type == 'PREFERENCES': + continue + space = area.spaces.active + # Ignore the same region type multiple times in an area. + # Prevents the 3D-viewport quad-view from attempting to expand 3 extra times for e.g. + region_type_unique = set() + for region in area.regions: + if region.type not in {'WINDOW', 'PREVIEW'}: + continue + if region.type in region_type_unique: + continue + region_type_unique.add(region.type) + with context.temp_override(window=window, area=area, region=region): + yield from _rna_path_prop_search_for_context_impl(context, edit_text, unique_attrs) + + if not unique_attrs: + # Users *might* only have a preferences area shown, in that case just expand the current context. + yield from _rna_path_prop_search_for_context_impl(context, edit_text, unique_attrs) + + rna_path_prop = StringProperty( name="Context Attributes", - description="RNA context string", + description="Context data-path (expanded using visible windows in the current .blend file)", maxlen=1024, + search=rna_path_prop_search_for_context, ) rna_reverse_prop = BoolProperty( diff --git a/release/scripts/startup/bl_ui/properties_data_volume.py b/release/scripts/startup/bl_ui/properties_data_volume.py index 678972a677c..98855d4b516 100644 --- a/release/scripts/startup/bl_ui/properties_data_volume.py +++ b/release/scripts/startup/bl_ui/properties_data_volume.py @@ -115,6 +115,9 @@ class DATA_PT_volume_render(DataButtonsPanel, Panel): col = layout.column(align=True) col.prop(render, "clipping") + col = layout.column() + col.prop(render, "precision") + col = layout.column(align=False) col.prop(volume, "velocity_grid") diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 6b55683ee89..f186fca0849 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -245,7 +245,7 @@ class GPENCIL_MT_move_to_layer(Menu): icon = 'GREASEPENCIL' else: icon = 'NONE' - layout.operator("gpencil.move_to_layer", text=gpl.info, icon=icon).layer = i + layout.operator("gpencil.move_to_layer", text=gpl.info, icon=icon, translate=False).layer = i i -= 1 layout.separator() diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 31552111276..92cfa7e8219 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -696,11 +696,12 @@ class ASSETBROWSEROLD_PT_metadata(asset_utils.AssetBrowserPanel, Panel): prefs = context.preferences show_asset_debug_info = prefs.view.show_developer_ui and prefs.experimental.show_asset_debug_info + is_local_asset = bool(asset_file_handle.local_id) layout.use_property_split = True layout.use_property_decorate = False # No animation. - if asset_file_handle.local_id: + if is_local_asset: # If the active file is an ID, use its name directly so renaming is possible from right here. layout.prop(asset_file_handle.local_id, "name") @@ -720,7 +721,7 @@ class ASSETBROWSEROLD_PT_metadata(asset_utils.AssetBrowserPanel, Panel): col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name") row = layout.row(align=True) - row.prop(wm, "asset_path_dummy", text="Source") + row.prop(wm, "asset_path_dummy", text="Source", icon='CURRENT_FILE' if is_local_asset else 'NONE') row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS') layout.prop(asset_file_handle.asset_data, "description") diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index bcc5976fb59..1af70895be9 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2295,6 +2295,8 @@ class VIEW3D_MT_object_relations(Menu): layout = self.layout layout.operator("object.make_override_library", text="Make Library Override...") + layout.operator("object.make_override_library", + text="Make Library Override - Fully Editable...").do_fully_editable = True layout.operator("object.make_dupli_face") diff --git a/release/scripts/templates_py/gizmo_operator.py b/release/scripts/templates_py/gizmo_operator.py index 450f67ba3a4..75595b0c5ea 100644 --- a/release/scripts/templates_py/gizmo_operator.py +++ b/release/scripts/templates_py/gizmo_operator.py @@ -84,7 +84,7 @@ class SelectSideOfPlaneGizmoGroup(GizmoGroup): bl_label = "Side of Plane Gizmo" bl_space_type = 'VIEW_3D' bl_region_type = 'WINDOW' - bl_options = {'3D'} + bl_options = {'3D', 'EXCLUDE_MODAL'} # Helper functions @staticmethod @@ -199,7 +199,8 @@ class SelectSideOfPlaneGizmoGroup(GizmoGroup): matrix.col[0].xyz = no_x matrix.col[1].xyz = no_y matrix.col[2].xyz = no_z - matrix.col[3].xyz = co + # The location callback handles the location. + # `matrix.col[3].xyz = co`. # Dial no_z = self.rotate_axis diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 8f33003fa9d..d22abd235df 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -157,25 +157,6 @@ BVHTree *bvhtree_from_mesh_edges_ex(struct BVHTreeFromMesh *data, int tree_type, int axis); -/** - * Builds a BVH-tree where nodes are the given tessellated faces - * (NOTE: does not copy given mfaces!). - * \param vert_allocated: if true, vert freeing will be done when freeing data. - * \param face_allocated: if true, face freeing will be done when freeing data. - * \param faces_mask: if not null, true elements give which faces to add to BVH-tree. - * \param faces_num_active: if >= 0, number of active faces to add to BVH-tree - * (else will be computed from mask). - */ -BVHTree *bvhtree_from_mesh_faces_ex(struct BVHTreeFromMesh *data, - const struct MVert *vert, - const struct MFace *face, - int numFaces, - const BLI_bitmap *faces_mask, - int faces_num_active, - float epsilon, - int tree_type, - int axis); - BVHTree *bvhtree_from_editmesh_looptri( BVHTreeFromEditMesh *data, struct BMEditMesh *em, float epsilon, int tree_type, int axis); @@ -192,8 +173,6 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, /** * Builds a BVH-tree where nodes are the looptri faces of the given mesh. - * - * \note for edit-mesh this is currently a duplicate of #bvhtree_from_mesh_faces_ex */ BVHTree *bvhtree_from_mesh_looptri_ex(struct BVHTreeFromMesh *data, const struct MVert *vert, diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index d32f754c6c0..a21d9141ac0 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -263,7 +263,9 @@ void BKE_defvert_extract_vgroup_to_polyweights(const struct MDeformVert *dvert, void BKE_defvert_weight_to_rgb(float r_rgb[3], float weight); -void BKE_defvert_blend_write(struct BlendWriter *writer, int count, struct MDeformVert *dvlist); +void BKE_defvert_blend_write(struct BlendWriter *writer, + int count, + const struct MDeformVert *dvlist); void BKE_defvert_blend_read(struct BlendDataReader *reader, int count, struct MDeformVert *mdverts); diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 06feb07aef2..96b6f7a53b0 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -81,6 +81,7 @@ typedef struct Global { * * 1 - 30: EEVEE debug/stats values (01/2018). * * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021). * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). + * * 102: Enable extra items in string search UI (05/2022). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). * * 799: Enable some mysterious new depsgraph behavior (05/2015). diff --git a/source/blender/blenkernel/BKE_image_partial_update.hh b/source/blender/blenkernel/BKE_image_partial_update.hh index 393bf003caa..6611efe7a61 100644 --- a/source/blender/blenkernel/BKE_image_partial_update.hh +++ b/source/blender/blenkernel/BKE_image_partial_update.hh @@ -172,6 +172,11 @@ class ImageTileData : AbstractTileData { if (image_user != nullptr) { this->image_user = *image_user; } + else { + /* When no image user is given the lastframe of the image should be used. This reflect the + * same logic when using a stencil image in the clone tool. */ + this->image_user.framenr = image->lastframe; + } } void init_data(TileNumber new_tile_number) override diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index dfb2b900b10..38de4bebdbd 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -116,6 +116,8 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * \param do_no_main: Create the new override data outside of Main database. * Used for resyncing of linked overrides. * + * \param do_fully_editable: if true, tag all created overrides as user-editable by default. + * * \return \a true on success, \a false otherwise. */ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, @@ -123,7 +125,8 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, const struct ID *id_root_reference, struct ID *id_hierarchy_root, const struct ID *id_hierarchy_root_reference, - bool do_no_main); + bool do_no_main, + const bool do_fully_editable); /** * Advanced 'smart' function to create fully functional overrides. * @@ -154,6 +157,8 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, * * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. * + * \param do_fully_editable: if true, tag all created overrides as user-editable by default. + * * \return true if override was successfully created. */ bool BKE_lib_override_library_create(struct Main *bmain, @@ -163,7 +168,8 @@ bool BKE_lib_override_library_create(struct Main *bmain, struct ID *id_root_reference, struct ID *id_hierarchy_root_reference, struct ID *id_instance_hint, - struct ID **r_id_root_override); + struct ID **r_id_root_override, + const bool do_fully_editable); /** * Create a library override template. */ diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index be4b671ef75..ebf48acde0f 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -458,17 +458,6 @@ static ID *loose_data_instantiate_process_check(LooseDataInstantiateContext *ins return NULL; } - if (item->action == LINK_APPEND_ACT_COPY_LOCAL) { - BLI_assert(ID_IS_LINKED(id)); - id = id->newid; - if (id == NULL) { - return NULL; - } - - BLI_assert(!ID_IS_LINKED(id)); - return id; - } - BLI_assert(!ID_IS_LINKED(id)); return id; } @@ -1178,7 +1167,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * for (itemlink = lapp_context->items.list; itemlink; itemlink = itemlink->next) { BlendfileLinkAppendContextItem *item = itemlink->link; - if (item->action != LINK_APPEND_ACT_REUSE_LOCAL) { + if (!ELEM(item->action, LINK_APPEND_ACT_COPY_LOCAL, LINK_APPEND_ACT_REUSE_LOCAL)) { continue; } @@ -1189,13 +1178,15 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * BLI_assert(ID_IS_LINKED(id)); BLI_assert(id->newid != NULL); + /* Calling code may want to access newly appended IDs from the link/append context items. */ + item->new_id = id->newid; + /* Do NOT delete a linked data that was already linked before this append. */ if (id->tag & LIB_TAG_PRE_EXISTING) { continue; } id->tag |= LIB_TAG_DOIT; - item->new_id = id->newid; } BKE_id_multi_tagged_delete(bmain); @@ -1204,26 +1195,6 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * .active_collection = NULL}; loose_data_instantiate(&instantiate_context); - /* Attempt to deal with object proxies. - * - * NOTE: Copied from `BKE_library_make_local`, but this is not really working (as in, not - * producing any useful result in any known use case), neither here nor in - * `BKE_library_make_local` currently. - * Proxies are end of life anyway, so not worth spending time on this. */ - for (itemlink = lapp_context->items.list; itemlink; itemlink = itemlink->next) { - BlendfileLinkAppendContextItem *item = itemlink->link; - - if (item->action != LINK_APPEND_ACT_COPY_LOCAL) { - continue; - } - - ID *id = item->new_id; - if (id == NULL) { - continue; - } - BLI_assert(ID_IS_LINKED(id)); - } - BKE_main_id_newptr_and_tag_clear(bmain); blendfile_link_append_proxies_convert(bmain, reports); diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 4d95cdd1e02..35c2039634a 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -967,31 +967,6 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon, return tree; } -BVHTree *bvhtree_from_mesh_faces_ex(BVHTreeFromMesh *data, - const MVert *vert, - const MFace *face, - const int numFaces, - const BLI_bitmap *faces_mask, - int faces_num_active, - float epsilon, - int tree_type, - int axis) -{ - BVHTree *tree = nullptr; - tree = bvhtree_from_mesh_faces_create_tree( - epsilon, tree_type, axis, vert, face, numFaces, faces_mask, faces_num_active); - - bvhtree_balance(tree, false); - - if (data) { - /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_setup_data( - tree, BVHTREE_FROM_FACES, vert, nullptr, face, nullptr, nullptr, nullptr, data); - } - - return tree; -} - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index ed11e5359ca..27e8bf96dc6 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -5149,12 +5149,12 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, /** \name Custom Data IO * \{ */ -static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) +static void write_mdisps(BlendWriter *writer, int count, const MDisps *mdlist, int external) { if (mdlist) { BLO_write_struct_array(writer, MDisps, count, mdlist); for (int i = 0; i < count; i++) { - MDisps *md = &mdlist[i]; + const MDisps *md = &mdlist[i]; if (md->disps) { if (!external) { BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]); @@ -5168,12 +5168,14 @@ static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int ext } } -static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask) +static void write_grid_paint_mask(BlendWriter *writer, + int count, + const GridPaintMask *grid_paint_mask) { if (grid_paint_mask) { BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask); for (int i = 0; i < count; i++) { - GridPaintMask *gpm = &grid_paint_mask[i]; + const GridPaintMask *gpm = &grid_paint_mask[i]; if (gpm->data) { const int gridsize = BKE_ccg_gridsize(gpm->level); BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data); @@ -5201,11 +5203,11 @@ void CustomData_blend_write(BlendWriter *writer, if (layer->type == CD_MDEFORMVERT) { /* layer types that allocate own memory need special handling */ - BKE_defvert_blend_write(writer, count, static_cast<struct MDeformVert *>(layer->data)); + BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer->data)); } else if (layer->type == CD_MDISPS) { write_mdisps( - writer, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL); + writer, count, static_cast<const MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL); } else if (layer->type == CD_PAINT_MASK) { const float *layer_data = static_cast<const float *>(layer->data); @@ -5216,7 +5218,7 @@ void CustomData_blend_write(BlendWriter *writer, BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); } else if (layer->type == CD_GRID_PAINT_MASK) { - write_grid_paint_mask(writer, count, static_cast<GridPaintMask *>(layer->data)); + write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer->data)); } else if (layer->type == CD_FACEMAP) { const int *layer_data = static_cast<const int *>(layer->data); diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index c7ac82e4b68..ebe06fa85eb 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -1561,7 +1561,7 @@ void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase) } } -void BKE_defvert_blend_write(BlendWriter *writer, int count, MDeformVert *dvlist) +void BKE_defvert_blend_write(BlendWriter *writer, int count, const MDeformVert *dvlist) { if (dvlist == NULL) { return; diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index d23918215ba..9b023d12a7d 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -135,12 +135,12 @@ const Curve *CurveComponent::get_curve_for_render() const /** \} */ +namespace blender::bke { + /* -------------------------------------------------------------------- */ /** \name Curve Normals Access * \{ */ -namespace blender::bke { - static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves) { const VArray<int8_t> types = curves.curve_types(); @@ -300,10 +300,10 @@ bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; } -} // namespace blender::bke - /** \} */ +} // namespace blender::bke + /* -------------------------------------------------------------------- */ /** \name Attribute Access Helper Functions * \{ */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 041696fa8d3..dd22d64aee1 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -236,6 +236,7 @@ static int stroke_march_next_point(const bGPDstroke *gps, } else { next_point_index = gps->totpoints - 1; + remaining_till_next = 0; break; } } @@ -263,15 +264,18 @@ static int stroke_march_next_point(const bGPDstroke *gps, float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); *ratio_result = ratio; + float d1 = len_v3v3(result, &gps->points[*index_from].x); + float d2 = len_v3v3(result, &gps->points[next_point_index].x); + float vratio = d1 / (d1 + d2); *pressure = interpf( - gps->points[next_point_index].pressure, gps->points[*index_from].pressure, ratio); + gps->points[next_point_index].pressure, gps->points[*index_from].pressure, vratio); *strength = interpf( - gps->points[next_point_index].strength, gps->points[*index_from].strength, ratio); + gps->points[next_point_index].strength, gps->points[*index_from].strength, vratio); interp_v4_v4v4(vert_color, gps->points[*index_from].vert_color, gps->points[next_point_index].vert_color, - ratio); + vratio); return next_point_index == 0 ? gps->totpoints : next_point_index; } @@ -320,6 +324,7 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, } else { next_point_index = gps->totpoints - 1; + remaining_till_next = 0; break; } } diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 3d894f47ae0..bc21e706d1e 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -182,9 +182,7 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - image_dst->gputexture[i][eye][resolution] = nullptr; - } + image_dst->gputexture[i][eye] = nullptr; } } @@ -236,24 +234,21 @@ static void image_foreach_cache(ID *id, key.offset_in_ID = offsetof(Image, cache); function_callback(id, &key, (void **)&image->cache, 0, user_data); - auto gputexture_offset = [image](int target, int eye, int resolution) { + auto gputexture_offset = [image](int target, int eye) { constexpr size_t base_offset = offsetof(Image, gputexture); - struct GPUTexture **first = &image->gputexture[0][0][0]; - const size_t array_offset = sizeof(*first) * - (&image->gputexture[target][eye][resolution] - first); + struct GPUTexture **first = &image->gputexture[0][0]; + const size_t array_offset = sizeof(*first) * (&image->gputexture[target][eye] - first); return base_offset + array_offset; }; for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *texture = image->gputexture[a][eye][resolution]; - if (texture == nullptr) { - continue; - } - key.offset_in_ID = gputexture_offset(a, eye, resolution); - function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); + GPUTexture *texture = image->gputexture[a][eye]; + if (texture == nullptr) { + continue; } + key.offset_in_ID = gputexture_offset(a, eye); + function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data); } } @@ -335,9 +330,7 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres ima->runtime.partial_update_user = nullptr; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - ima->gputexture[i][j][resolution] = nullptr; - } + ima->gputexture[i][j] = nullptr; } } @@ -784,10 +777,8 @@ bool BKE_image_has_opengl_texture(Image *ima) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - return true; - } + if (ima->gputexture[i][eye] != nullptr) { + return true; } } } @@ -2864,11 +2855,9 @@ static void image_free_tile(Image *ima, ImageTile *tile) } for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - ima->gputexture[i][eye][resolution] = nullptr; - } + if (ima->gputexture[i][eye] != nullptr) { + GPU_texture_free(ima->gputexture[i][eye]); + ima->gputexture[i][eye] = nullptr; } } } @@ -3208,16 +3197,14 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = nullptr; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = nullptr; - } + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = nullptr; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = nullptr; } } BKE_image_partial_update_mark_full_update(ima); @@ -3273,17 +3260,14 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = nullptr; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = nullptr; - } + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = nullptr; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = nullptr; } } BKE_image_partial_update_mark_full_update(ima); diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 0d470c5b663..bed79a318e8 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -38,7 +38,6 @@ extern "C" { /* Prototypes. */ static void gpu_free_unused_buffers(); static void image_free_gpu(Image *ima, const bool immediate); -static void image_free_gpu_limited_scale(Image *ima); static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); @@ -68,22 +67,19 @@ bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) /** \name UDIM GPU Texture * \{ */ -static bool is_over_resolution_limit(int w, int h, bool limit_gl_texture_size) +static bool is_over_resolution_limit(int w, int h) { - return (w > GPU_texture_size_with_limit(w, limit_gl_texture_size) || - h > GPU_texture_size_with_limit(h, limit_gl_texture_size)); + return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h)); } -static int smaller_power_of_2_limit(int num, bool limit_gl_texture_size) +static int smaller_power_of_2_limit(int num) { - return power_of_2_min_i(GPU_texture_size_with_limit(num, limit_gl_texture_size)); + return power_of_2_min_i(GPU_texture_size_with_limit(num)); } -static GPUTexture *gpu_texture_create_tile_mapping( - Image *ima, const int multiview_eye, const eImageTextureResolution texture_resolution) +static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) { - const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; - GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye][resolution]; + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; if (tilearray == nullptr) { return nullptr; @@ -105,7 +101,7 @@ static GPUTexture *gpu_texture_create_tile_mapping( } LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { int i = tile->tile_number - 1001; - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; data[4 * i] = tile_runtime->tilearray_layer; float *tile_info = &data[4 * width + 4 * i]; @@ -137,12 +133,8 @@ static int compare_packtile(const void *a, const void *b) return tile_a->pack_score < tile_b->pack_score; } -static GPUTexture *gpu_texture_create_tile_array(Image *ima, - ImBuf *main_ibuf, - const eImageTextureResolution texture_resolution) +static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) { - const bool limit_gl_texture_size = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED; - const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; int arraywidth = 0, arrayheight = 0; ListBase boxes = {nullptr}; @@ -158,10 +150,9 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, packtile->boxpack.w = ibuf->x; packtile->boxpack.h = ibuf->y; - if (is_over_resolution_limit( - packtile->boxpack.w, packtile->boxpack.h, limit_gl_texture_size)) { - packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w, limit_gl_texture_size); - packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h, limit_gl_texture_size); + if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) { + packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); + packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); } arraywidth = max_ii(arraywidth, packtile->boxpack.w); arrayheight = max_ii(arrayheight, packtile->boxpack.h); @@ -188,7 +179,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, LISTBASE_FOREACH (PackTile *, packtile, &packed) { ImageTile *tile = packtile->tile; - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tileoffset = tile_runtime->tilearray_offset; int *tilesize = tile_runtime->tilearray_size; @@ -210,7 +201,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, /* Upload each tile one by one. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int tilelayer = tile_runtime->tilearray_layer; int *tileoffset = tile_runtime->tilearray_offset; int *tilesize = tile_runtime->tilearray_size; @@ -258,33 +249,16 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, /** \name Regular gpu texture * \{ */ -static bool image_max_resolution_texture_fits_in_limited_scale(Image *ima, - eGPUTextureTarget textarget, - const int multiview_eye) -{ - BLI_assert_msg(U.glreslimit != 0, - "limited scale function called without limited scale being set."); - GPUTexture *max_resolution_texture = - ima->gputexture[textarget][multiview_eye][IMA_TEXTURE_RESOLUTION_FULL]; - if (max_resolution_texture && GPU_texture_width(max_resolution_texture) <= U.glreslimit && - GPU_texture_height(max_resolution_texture) <= U.glreslimit) { - return true; - } - return false; -} - static GPUTexture **get_image_gpu_texture_ptr(Image *ima, eGPUTextureTarget textarget, - const int multiview_eye, - const eImageTextureResolution texture_resolution) + const int multiview_eye) { const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); BLI_assert(in_range); BLI_assert(ELEM(multiview_eye, 0, 1)); - const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; if (in_range) { - return &(ima->gputexture[textarget][multiview_eye][resolution]); + return &(ima->gputexture[textarget][multiview_eye]); } return nullptr; } @@ -303,21 +277,6 @@ static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) } } -static void image_update_reusable_textures(Image *ima, - eGPUTextureTarget textarget, - const int multiview_eye) -{ - if ((ima->gpuflag & IMA_GPU_HAS_LIMITED_SCALE_TEXTURES) == 0) { - return; - } - - if (ELEM(textarget, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { - if (image_max_resolution_texture_fits_in_limited_scale(ima, textarget, multiview_eye)) { - image_free_gpu_limited_scale(ima); - } - } -} - static void image_gpu_texture_partial_update_changes_available( Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) { @@ -412,14 +371,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (current_view >= 2) { current_view = 0; } - const bool limit_resolution = U.glreslimit != 0 && - ((iuser && (iuser->flag & IMA_SHOW_MAX_RESOLUTION) == 0) || - (iuser == nullptr)) && - ((ima->gpuflag & IMA_GPU_REUSE_MAX_RESOLUTION) == 0); - const eImageTextureResolution texture_resolution = limit_resolution ? - IMA_TEXTURE_RESOLUTION_LIMITED : - IMA_TEXTURE_RESOLUTION_FULL; - GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view, texture_resolution); + GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); if (*tex) { return *tex; } @@ -443,11 +395,10 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } if (textarget == TEXTARGET_2D_ARRAY) { - *tex = gpu_texture_create_tile_array(ima, ibuf_intern, texture_resolution); + *tex = gpu_texture_create_tile_array(ima, ibuf_intern); } else if (textarget == TEXTARGET_TILE_MAPPING) { - *tex = gpu_texture_create_tile_mapping( - ima, iuser ? iuser->multiview_eye : 0, texture_resolution); + *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); } else { const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); @@ -455,7 +406,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, ibuf_intern); *tex = IMB_create_gpu_texture( - ima->id.name + 2, ibuf_intern, use_high_bitdepth, store_premultiplied, limit_resolution); + ima->id.name + 2, ibuf_intern, use_high_bitdepth, store_premultiplied); if (*tex) { GPU_texture_wrap_mode(*tex, true, false); @@ -473,20 +424,6 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } } - switch (texture_resolution) { - case IMA_TEXTURE_RESOLUTION_LIMITED: - ima->gpuflag |= IMA_GPU_HAS_LIMITED_SCALE_TEXTURES; - break; - - case IMA_TEXTURE_RESOLUTION_FULL: - image_update_reusable_textures(ima, textarget, current_view); - break; - - case IMA_TEXTURE_RESOLUTION_LEN: - BLI_assert_unreachable(); - break; - } - if (*tex) { GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); } @@ -558,39 +495,22 @@ static void image_free_gpu(Image *ima, const bool immediate) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - if (immediate) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - } - else { - BLI_mutex_lock(&gpu_texture_queue_mutex); - BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye][resolution]); - BLI_mutex_unlock(&gpu_texture_queue_mutex); - } - - ima->gputexture[i][eye][resolution] = nullptr; + if (ima->gputexture[i][eye] != nullptr) { + if (immediate) { + GPU_texture_free(ima->gputexture[i][eye]); + } + else { + BLI_mutex_lock(&gpu_texture_queue_mutex); + BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); + BLI_mutex_unlock(&gpu_texture_queue_mutex); } - } - } - } - - ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); -} -static void image_free_gpu_limited_scale(Image *ima) -{ - const eImageTextureResolution resolution = IMA_TEXTURE_RESOLUTION_LIMITED; - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - ima->gputexture[i][eye][resolution] = nullptr; + ima->gputexture[i][eye] = nullptr; } } } - ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; } void BKE_image_free_gputextures(Image *ima) @@ -767,20 +687,12 @@ static void gpu_texture_update_unscaled(GPUTexture *tex, GPU_unpack_row_length_set(0); } -static void gpu_texture_update_from_ibuf(GPUTexture *tex, - Image *ima, - ImBuf *ibuf, - ImageTile *tile, - int x, - int y, - int w, - int h, - eImageTextureResolution texture_resolution) +static void gpu_texture_update_from_ibuf( + GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) { - const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; bool scaled; if (tile != nullptr) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tilesize = tile_runtime->tilearray_size; scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); } @@ -845,7 +757,7 @@ static void gpu_texture_update_from_ibuf(GPUTexture *tex, if (scaled) { /* Slower update where we first have to scale the input pixels. */ if (tile != nullptr) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tileoffset = tile_runtime->tilearray_offset; int *tilesize = tile_runtime->tilearray_size; int tilelayer = tile_runtime->tilearray_layer; @@ -860,7 +772,7 @@ static void gpu_texture_update_from_ibuf(GPUTexture *tex, else { /* Fast update at same resolution. */ if (tile != nullptr) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tileoffset = tile_runtime->tilearray_offset; int tilelayer = tile_runtime->tilearray_layer; gpu_texture_update_unscaled( @@ -894,19 +806,16 @@ static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h) { const int eye = 0; - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye][resolution]; - eImageTextureResolution texture_resolution = static_cast<eImageTextureResolution>(resolution); - /* Check if we need to update the main gputexture. */ - if (tex != nullptr && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, nullptr, x, y, w, h, texture_resolution); - } + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye]; + /* Check if we need to update the main gputexture. */ + if (tex != nullptr && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, nullptr, x, y, w, h); + } - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]; - if (tex != nullptr) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h, texture_resolution); - } + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye]; + if (tex != nullptr) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } } @@ -946,11 +855,9 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) for (int a = 0; a < TEXTARGET_COUNT; a++) { if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *tex = ima->gputexture[a][eye][resolution]; - if (tex != nullptr) { - GPU_texture_mipmap_mode(tex, mipmap, true); - } + GPUTexture *tex = ima->gputexture[a][eye]; + if (tex != nullptr) { + GPU_texture_mipmap_mode(tex, mipmap, true); } } } diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 0230cf89d06..b67d3490e03 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -143,7 +143,7 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; - if (ima->source == IMA_SRC_TILED) { + if (ibuf->name[0] == '\0' || ima->source == IMA_SRC_TILED) { BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); } diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 904039d56c8..f14c11a949e 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -6,6 +6,8 @@ * Contains management of ID's for freeing & deletion. */ +#include "CLG_log.h" + #include "MEM_guardedalloc.h" /* all types are needed here, in order to do memory operations */ @@ -35,8 +37,7 @@ # include "BPY_extern.h" #endif -/* Not used currently. */ -// static CLG_LogRef LOG = {.identifier = "bke.lib_id_delete"}; +static CLG_LogRef LOG = {.identifier = "bke.lib_id_delete"}; void BKE_libblock_free_data(ID *id, const bool do_id_user) { @@ -334,11 +335,13 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) for (id = do_tagged_deletion ? tagged_deleted_ids.first : lb->first; id; id = id_next) { id_next = id->next; if (id->tag & tag) { - if (id->us != 0) { -#ifdef DEBUG_PRINT - printf("%s: deleting %s (%d)\n", __func__, id->name, id->us); -#endif - BLI_assert(id->us == 0); + if (((id->tag & LIB_TAG_EXTRAUSER_SET) == 0 && ID_REAL_USERS(id) != 0) || + ((id->tag & LIB_TAG_EXTRAUSER_SET) != 0 && ID_REAL_USERS(id) != 1)) { + CLOG_ERROR(&LOG, + "Deleting %s which still has %d users (including %d 'extra' shallow users)\n", + id->name, + ID_REAL_USERS(id), + (id->tag & LIB_TAG_EXTRAUSER_SET) != 0 ? 1 : 0); } BKE_id_free_ex(bmain, id, free_flag, !do_tagged_deletion); ++num_datablocks_deleted; diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 6dd13952413..50c9514e810 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -398,7 +398,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, const ID *id_root_reference, ID *id_hierarchy_root, const ID *id_hierarchy_root_reference, - const bool do_no_main) + const bool do_no_main, + const bool do_fully_editable) { BLI_assert(id_root_reference != NULL && ID_IS_LINKED(id_root_reference)); /* If we do not have any hierarchy root given, then the root reference must be tagged for @@ -464,6 +465,9 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, success = false; break; } + if (do_fully_editable) { + reference_id->newid->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } } /* We also tag the new IDs so that in next step we can remap their pointers too. */ reference_id->newid->tag |= LIB_TAG_DOIT; @@ -993,7 +997,8 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, Library *owner_library, ID *id_root_reference, - ID *id_hierarchy_root_reference) + ID *id_hierarchy_root_reference, + const bool do_fully_editable) { BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, @@ -1017,12 +1022,22 @@ static bool lib_override_library_create_do(Main *bmain, BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == id_root_reference->lib); - success = BKE_lib_override_library_create_from_tag( - bmain, owner_library, id_root_reference, id_hierarchy_root_reference, NULL, false); + success = BKE_lib_override_library_create_from_tag(bmain, + owner_library, + id_root_reference, + id_hierarchy_root_reference, + NULL, + false, + do_fully_editable); } else { - success = BKE_lib_override_library_create_from_tag( - bmain, owner_library, id_root_reference, NULL, id_hierarchy_root_reference, false); + success = BKE_lib_override_library_create_from_tag(bmain, + owner_library, + id_root_reference, + NULL, + id_hierarchy_root_reference, + false, + do_fully_editable); } return success; @@ -1180,7 +1195,8 @@ bool BKE_lib_override_library_create(Main *bmain, ID *id_root_reference, ID *id_hierarchy_root_reference, ID *id_instance_hint, - ID **r_id_root_override) + ID **r_id_root_override, + const bool do_fully_editable) { if (r_id_root_override != NULL) { *r_id_root_override = NULL; @@ -1190,8 +1206,12 @@ bool BKE_lib_override_library_create(Main *bmain, id_hierarchy_root_reference = id_root_reference; } - const bool success = lib_override_library_create_do( - bmain, scene, owner_library, id_root_reference, id_hierarchy_root_reference); + const bool success = lib_override_library_create_do(bmain, + scene, + owner_library, + id_root_reference, + id_hierarchy_root_reference, + do_fully_editable); if (!success) { return success; @@ -1680,7 +1700,13 @@ static bool lib_override_library_resync(Main *bmain, * override IDs (including within the old overrides themselves, since those are tagged too * above). */ const bool success = BKE_lib_override_library_create_from_tag( - bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, NULL, true); + bmain, + NULL, + id_root_reference, + id_root->override_library->hierarchy_root, + NULL, + true, + false); if (!success) { BLI_ghash_free(linkedref_to_old_override, NULL, NULL); diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c index 5e9d8e8c4d0..88f6fbb0ead 100644 --- a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c +++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c @@ -39,7 +39,8 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, /* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is * coming. */ Object *ob_proxy_group = ob_proxy->proxy_group; - const bool is_override_instancing_object = ob_proxy_group != NULL; + const bool is_override_instancing_object = (ob_proxy_group != NULL) && + (ob_proxy_group->instance_collection != NULL); ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : &ob_proxy->proxy->id; ID *id_instance_hint = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; @@ -82,7 +83,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, FOREACH_MAIN_ID_END; return BKE_lib_override_library_create( - bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_root, id_instance_hint, NULL); + bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_root, id_instance_hint, NULL, false); } static void lib_override_library_proxy_convert_do(Main *bmain, diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index accbca42da6..19ee2ba6605 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -2085,8 +2085,7 @@ GPUTexture *BKE_movieclip_get_gpu_texture(MovieClip *clip, MovieClipUser *cuser) /* This only means RGBA16F instead of RGBA32F. */ const bool high_bitdepth = false; const bool store_premultiplied = ibuf->rect_float ? false : true; - *tex = IMB_create_gpu_texture( - clip->id.name + 2, ibuf, high_bitdepth, store_premultiplied, false); + *tex = IMB_create_gpu_texture(clip->id.name + 2, ibuf, high_bitdepth, store_premultiplied); /* Do not generate mips for movieclips... too slow. */ GPU_texture_mipmap_mode(*tex, false, true); diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 5d804f53779..a5f6c453ed4 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -244,24 +244,38 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const } } -/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */ -static void update_active_strip(AnimData *adt_dest, - NlaTrack *track_dest, - const AnimData *adt_source, - NlaTrack *track_source) +static void update_active_strip_from_listbase(AnimData *adt_dest, + NlaTrack *track_dest, + const NlaStrip *active_strip, + const ListBase /* NlaStrip */ *strips_source) { - BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips)); - NlaStrip *strip_dest = track_dest->strips.first; - LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) { - if (strip_source == adt_source->actstrip) { + LISTBASE_FOREACH (const NlaStrip *, strip_source, strips_source) { + if (strip_source == active_strip) { adt_dest->actstrip = strip_dest; + return; + } + + if (strip_source->type == NLASTRIP_TYPE_META) { + update_active_strip_from_listbase(adt_dest, track_dest, active_strip, &strip_source->strips); } strip_dest = strip_dest->next; } } +/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */ +static void update_active_strip(AnimData *adt_dest, + NlaTrack *track_dest, + const AnimData *adt_source, + const NlaTrack *track_source) +{ + BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips)); + + update_active_strip_from_listbase( + adt_dest, track_dest, adt_source->actstrip, &track_source->strips); +} + /* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */ static void update_active_track(AnimData *adt_dest, const AnimData *adt_source) { @@ -272,20 +286,20 @@ static void update_active_track(AnimData *adt_dest, const AnimData *adt_source) LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) { if (track_source == adt_source->act_track) { adt_dest->act_track = track_dest; - /* Assumption: the active strip is on the active track. */ - update_active_strip(adt_dest, track_dest, adt_source, track_source); } + update_active_strip(adt_dest, track_dest, adt_source, track_source); track_dest = track_dest->next; } - /* If the above assumption failed to hold, do a more thorough search for the active strip. */ - if (adt_source->actstrip != NULL && adt_dest->actstrip == NULL) { - nla_tweakmode_find_active(&adt_source->nla_tracks, &track_dest, &adt_dest->actstrip); +#ifndef NDEBUG + { + const bool source_has_actstrip = adt_source->actstrip != NULL; + const bool dest_has_actstrip = adt_dest->actstrip != NULL; + BLI_assert_msg(source_has_actstrip == dest_has_actstrip, + "Active strip did not copy correctly"); } - - BLI_assert_msg((adt_source->actstrip == NULL) == (adt_dest->actstrip == NULL), - "Active strip did not copy correctly"); +#endif } void BKE_nla_tracks_copy_from_adt(Main *bmain, @@ -1171,26 +1185,35 @@ bool BKE_nlatrack_is_nonlocal_in_liboverride(const ID *id, const NlaTrack *nlt) /* NLA Strips -------------------------------------- */ -NlaStrip *BKE_nlastrip_find_active(NlaTrack *nlt) +static NlaStrip *nlastrip_find_active(ListBase /* NlaStrip */ *strips) { - NlaStrip *strip; - - /* sanity check */ - if (ELEM(NULL, nlt, nlt->strips.first)) { - return NULL; - } - - /* try to find the first active strip */ - for (strip = nlt->strips.first; strip; strip = strip->next) { + LISTBASE_FOREACH (NlaStrip *, strip, strips) { if (strip->flag & NLASTRIP_FLAG_ACTIVE) { return strip; } + + if (strip->type != NLASTRIP_TYPE_META) { + continue; + } + + NlaStrip *inner_active = nlastrip_find_active(&strip->strips); + if (inner_active != NULL) { + return inner_active; + } } - /* none found */ return NULL; } +NlaStrip *BKE_nlastrip_find_active(NlaTrack *nlt) +{ + if (nlt == NULL) { + return NULL; + } + + return nlastrip_find_active(&nlt->strips); +} + void BKE_nlastrip_set_active(AnimData *adt, NlaStrip *strip) { NlaTrack *nlt; diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 8afe7ce7520..68e4cccba00 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -985,7 +985,7 @@ class NodeTreeMainUpdater { this->remove_unused_previews_when_necessary(ntree); this->ensure_tree_ref(ntree, tree_ref); - this->update_has_image_animation(*tree_ref); + this->propagate_runtime_flags(*tree_ref); if (ntree.type == NTREE_GEOMETRY) { if (node_field_inferencing::update_field_inferencing(*tree_ref)) { result.interface_changed = true; @@ -1256,10 +1256,10 @@ class NodeTreeMainUpdater { BKE_node_preview_remove_unused(&ntree); } - void update_has_image_animation(const NodeTreeRef &tree_ref) + void propagate_runtime_flags(const NodeTreeRef &tree_ref) { bNodeTree &ntree = *tree_ref.btree(); - ntree.runtime_flag &= ~NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION; + ntree.runtime_flag = 0; if (ntree.type != NTREE_SHADER) { return; } @@ -1268,21 +1268,30 @@ class NodeTreeMainUpdater { for (const NodeRef *group_node : tree_ref.nodes_by_type("NodeGroup")) { const bNodeTree *group = reinterpret_cast<bNodeTree *>(group_node->bnode()->id); if (group != nullptr) { - if (group->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) { - ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION; - return; - } + ntree.runtime_flag |= group->runtime_flag; } } /* Check if the tree itself has an animated image. */ - for (const StringRefNull idname : {"ShaderNodeTexImage", "ShaderNodeTexEnvironment"}) + for (const StringRefNull idname : {"ShaderNodeTexImage", "ShaderNodeTexEnvironment"}) { for (const NodeRef *node : tree_ref.nodes_by_type(idname)) { Image *image = reinterpret_cast<Image *>(node->bnode()->id); if (image != nullptr && BKE_image_is_animated(image)) { ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION; - return; + break; } } + } + /* Check if the tree has a material output. */ + for (const StringRefNull idname : {"ShaderNodeOutputMaterial", + "ShaderNodeOutputLight", + "ShaderNodeOutputWorld", + "ShaderNodeOutputAOV"}) { + const Span<const NodeRef *> nodes = tree_ref.nodes_by_type(idname); + if (!nodes.is_empty()) { + ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT; + break; + } + } } void update_node_levels(bNodeTree &ntree) @@ -1392,10 +1401,12 @@ class NodeTreeMainUpdater { return true; } /* Assume node groups without output sockets are outputs. */ - /* TODO: Store whether a node group contains a top-level output node (e.g. Material Output) in - * run-time information on the node group itself. */ - if (bnode.type == NODE_GROUP && node.outputs().is_empty()) { - return true; + if (bnode.type == NODE_GROUP) { + const bNodeTree *node_group = reinterpret_cast<const bNodeTree *>(bnode.id); + if (node_group != nullptr && + node_group->runtime_flag & NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT) { + return true; + } } return false; } diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 02134623a31..30e02e5411b 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -268,7 +268,7 @@ static struct bUnitCollection buImperialAclCollection = {buImperialAclDef, 0, 0, /* Time. */ static struct bUnitDef buNaturalTimeDef[] = { /* Weeks? - probably not needed for Blender. */ - {"day", "days", "d", NULL, "Days", "DAYS", 90000.0, 0.0, B_UNIT_DEF_NONE}, + {"day", "days", "d", NULL, "Days", "DAYS", 86400.0, 0.0, B_UNIT_DEF_NONE}, {"hour", "hours", "hr", "h", "Hours", "HOURS", 3600.0, 0.0, B_UNIT_DEF_NONE}, {"minute", "minutes", "min", "m", "Minutes", "MINUTES", 60.0, 0.0, B_UNIT_DEF_NONE}, {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 91dfc81ae27..813277d9968 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -64,10 +64,10 @@ class Array { int64_t size_; /** Used for allocations when the inline buffer is too small. */ - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; /** A placeholder buffer that will remain uninitialized until it is used. */ - TypedBuffer<T, InlineBufferCapacity> inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_; public: /** diff --git a/source/blender/blenlib/BLI_generic_array.hh b/source/blender/blenlib/BLI_generic_array.hh index e1b6b29874a..4b917434264 100644 --- a/source/blender/blenlib/BLI_generic_array.hh +++ b/source/blender/blenlib/BLI_generic_array.hh @@ -33,7 +33,7 @@ class GArray { void *data_ = nullptr; int64_t size_ = 0; - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; public: /** diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh index 6532c59a846..deb6ea3b5fd 100644 --- a/source/blender/blenlib/BLI_linear_allocator.hh +++ b/source/blender/blenlib/BLI_linear_allocator.hh @@ -18,7 +18,7 @@ namespace blender { template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopyable, NonMovable { private: - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; Vector<void *> owned_buffers_; Vector<Span<char>> unused_borrowed_buffers_; diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index d76aa46502d..55233676ed8 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -130,10 +130,10 @@ class Map { uint64_t slot_mask_; /** This is called to hash incoming keys. */ - Hash hash_; + BLI_NO_UNIQUE_ADDRESS Hash hash_; /** This is called to check equality of two keys. */ - IsEqual is_equal_; + BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_; /** The max load factor is 1/2 = 50% by default. */ #define LOAD_FACTOR 1, 2 diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index dcc00064e47..64b820a22b0 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -186,9 +186,6 @@ MINLINE void rgba_uchar_args_test_set( unsigned char col[4], unsigned char r, unsigned char g, unsigned char b, unsigned char a); MINLINE void cpack_cpy_3ub(unsigned char r_col[3], unsigned int pack); -void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max); -void wavelength_to_xyz_table(float *r_table, int width); - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index d7c41ae88a8..940542c9f1d 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -317,30 +317,36 @@ template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddr * An `AlignedBuffer` is a byte array with at least the given size and alignment. The buffer will * not be initialized by the default constructor. */ -template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { - private: - /* Don't create an empty array. This causes problems with some compilers. */ - char buffer_[(Size > 0) ? Size : 1]; +template<size_t Size, size_t Alignment> class AlignedBuffer { + struct Empty { + }; + struct alignas(Alignment) Sized { + /* Don't create an empty array. This causes problems with some compilers. */ + std::byte buffer_[Size > 0 ? Size : 1]; + }; + + using BufferType = std::conditional_t<Size == 0, Empty, Sized>; + BLI_NO_UNIQUE_ADDRESS BufferType buffer_; public: operator void *() { - return buffer_; + return this; } operator const void *() const { - return buffer_; + return this; } void *ptr() { - return buffer_; + return this; } const void *ptr() const { - return buffer_; + return this; } }; @@ -351,7 +357,7 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { */ template<typename T, int64_t Size = 1> class TypedBuffer { private: - AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_; + BLI_NO_UNIQUE_ADDRESS AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_; public: operator T *() diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 391d31c2228..62de4b79e41 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -136,10 +136,10 @@ class Set { uint64_t slot_mask_; /** This is called to hash incoming keys. */ - Hash hash_; + BLI_NO_UNIQUE_ADDRESS Hash hash_; /** This is called to check equality of two keys. */ - IsEqual is_equal_; + BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_; /** The max load factor is 1/2 = 50% by default. */ #define LOAD_FACTOR 1, 2 @@ -483,10 +483,10 @@ class Set { * while iterating over the set. However, after this method has been called, the removed element * must not be accessed anymore. */ - void remove(const Iterator &iterator) + void remove(const Iterator &it) { /* The const cast is valid because this method itself is not const. */ - Slot &slot = const_cast<Slot &>(iterator.current_slot()); + Slot &slot = const_cast<Slot &>(it.current_slot()); BLI_assert(slot.is_occupied()); slot.remove(); removed_slots_++; diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index a06515a7781..ed123f43a6b 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -96,7 +96,7 @@ class Stack { int64_t size_; /** The buffer used to implement small object optimization. */ - TypedBuffer<T, InlineBufferCapacity> inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_; /** * A chunk referencing the inline buffer. This is always the bottom-most chunk. @@ -105,7 +105,7 @@ class Stack { Chunk inline_chunk_; /** Used for allocations when the inline buffer is not large enough. */ - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; public: /** diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index b8407a5453f..7f9470a9111 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -843,6 +843,18 @@ extern bool BLI_memory_is_zero(const void *arr, size_t arr_size); */ #define BLI_ENABLE_IF(condition) typename std::enable_if_t<(condition)> * = nullptr +#if defined(_MSC_VER) +# define BLI_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(no_unique_address) +# define BLI_NO_UNIQUE_ADDRESS [[no_unique_address]] +# else +# define BLI_NO_UNIQUE_ADDRESS +# endif +#else +# define BLI_NO_UNIQUE_ADDRESS [[no_unique_address]] +#endif + /** \} */ #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index acf47f67168..c23d846d277 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -84,10 +84,10 @@ class Vector { T *capacity_end_; /** Used for allocations when the inline buffer is too small. */ - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; /** A placeholder buffer that will remain uninitialized until it is used. */ - TypedBuffer<T, InlineBufferCapacity> inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_; /** * Store the size of the vector explicitly in debug builds. Otherwise you'd always have to call diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 4ae1bf9000d..b0a3696f245 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -117,10 +117,10 @@ class VectorSet { uint64_t slot_mask_; /** This is called to hash incoming keys. */ - Hash hash_; + BLI_NO_UNIQUE_ADDRESS Hash hash_; /** This is called to check equality of two keys. */ - IsEqual is_equal_; + BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_; /** The max load factor is 1/2 = 50% by default. */ #define LOAD_FACTOR 1, 2 diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 52cbda82268..bdcf52ec521 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -597,158 +597,3 @@ void BLI_init_srgb_conversion(void) BLI_color_to_srgb_table[i] = (unsigned short)(b * 0x100); } } - -/* ****************************** blackbody ******************************** */ - -/* Calculate color in range 800..12000 using an approximation - * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B - * Max absolute error for RGB is (0.00095, 0.00077, 0.00057), - * which is enough to get the same 8 bit/channel color. - */ - -static const float blackbody_table_r[6][3] = { - {2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f}, - {3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f}, - {4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f}, - {4.66849800e+03f, 2.85655028e-05f, 1.29075375e-01f}, - {4.60124770e+03f, 2.89727618e-05f, 1.48001316e-01f}, - {3.78765709e+03f, 9.36026367e-06f, 3.98995841e-01f}, -}; - -static const float blackbody_table_g[6][3] = { - {-7.50343014e+02f, 3.15679613e-04f, 4.73464526e-01f}, - {-1.00402363e+03f, 1.29189794e-04f, 9.08181524e-01f}, - {-1.22075471e+03f, 2.56245413e-05f, 1.20753416e+00f}, - {-1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f}, - {-1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f}, - {-5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f}, -}; - -static const float blackbody_table_b[6][4] = { - {0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, - {-2.02524603e-11f, 1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f}, - {-2.22463426e-13f, -1.55078698e-08f, 3.81675160e-04f, -7.30646033e-01f}, - {6.72595954e-13f, -2.73059993e-08f, 4.24068546e-04f, -7.52204323e-01f}, -}; - -static void blackbody_temperature_to_rgb(float rgb[3], float t) -{ - if (t >= 12000.0f) { - rgb[0] = 0.826270103f; - rgb[1] = 0.994478524f; - rgb[2] = 1.56626022f; - } - else if (t < 965.0f) { - rgb[0] = 4.70366907f; - rgb[1] = 0.0f; - rgb[2] = 0.0f; - } - else { - int i = (t >= 6365.0f) ? 5 : - (t >= 3315.0f) ? 4 : - (t >= 1902.0f) ? 3 : - (t >= 1449.0f) ? 2 : - (t >= 1167.0f) ? 1 : - 0; - - const float *r = blackbody_table_r[i]; - const float *g = blackbody_table_g[i]; - const float *b = blackbody_table_b[i]; - - const float t_inv = 1.0f / t; - rgb[0] = r[0] * t_inv + r[1] * t + r[2]; - rgb[1] = g[0] * t_inv + g[1] * t + g[2]; - rgb[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3]; - } -} - -void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max) -{ - for (int i = 0; i < width; i++) { - float temperature = min + (max - min) / (float)width * (float)i; - - float rgb[3]; - blackbody_temperature_to_rgb(rgb, temperature); - - copy_v3_v3(&r_table[i * 4], rgb); - r_table[i * 4 + 3] = 0.0f; - } -} - -/* ****************************** wavelength ******************************** */ -/* Wavelength to RGB. */ - -/** - * CIE color matching functions `xBar`, `yBar`, and `zBar` for - * wavelengths from 380 through 780 nanometers, every 5 nanometers. - * - * For a wavelength lambda in this range: - * \code{.txt} - * cie_color_match[(lambda - 380) / 5][0] = xBar - * cie_color_match[(lambda - 380) / 5][1] = yBar - * cie_color_match[(lambda - 380) / 5][2] = zBar - * \endcode - */ - -static float cie_colour_match[81][3] = { - {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, - {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, - {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, - {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, - {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, - {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, - {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, - {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, - {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, - {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, - {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, - {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, - {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, - {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, - {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, - {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, - {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, - {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, - {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, - {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, - {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, - {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, - {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, - {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, - {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, - {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, - {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; - -static void wavelength_to_xyz(float xyz[3], float lambda_nm) -{ - float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */ - int i = (int)ii; - - if (i < 0 || i >= 80) { - xyz[0] = 0.0f; - xyz[1] = 0.0f; - xyz[2] = 0.0f; - } - else { - ii -= (float)i; - const float *c = cie_colour_match[i]; - xyz[0] = c[0] + ii * (c[3] - c[0]); - xyz[1] = c[1] + ii * (c[4] - c[1]); - xyz[2] = c[2] + ii * (c[5] - c[2]); - } -} - -void wavelength_to_xyz_table(float *r_table, int width) -{ - for (int i = 0; i < width; i++) { - float temperature = 380 + 400 / (float)width * (float)i; - - float rgb[3]; - wavelength_to_xyz(rgb, temperature); - - copy_v3_v3(&r_table[i * 4], rgb); - r_table[i * 4 + 3] = 0.0f; - } -} diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 108aa80ab1e..585ada3b2d8 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1666,13 +1666,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { - /* UV/Image Max resolution images in image editor. */ - if (space->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)space; - sima->iuser.flag |= IMA_SHOW_MAX_RESOLUTION; - } /* Enable Outliner render visibility column. */ - else if (space->spacetype == SPACE_OUTLINER) { + if (space->spacetype == SPACE_OUTLINER) { SpaceOutliner *space_outliner = (SpaceOutliner *)space; space_outliner->show_restrict_flags |= SO_RESTRICT_RENDER; } diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index a5693cb0fd7..3d539018cef 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC intern/builder/deg_builder_relations_view_layer.cc intern/builder/deg_builder_remove_noop.cc intern/builder/deg_builder_rna.cc + intern/builder/deg_builder_stack.cc intern/builder/deg_builder_transitive.cc intern/builder/pipeline.cc intern/builder/pipeline_all_objects.cc @@ -103,6 +104,7 @@ set(SRC intern/builder/deg_builder_relations_impl.h intern/builder/deg_builder_remove_noop.h intern/builder/deg_builder_rna.h + intern/builder/deg_builder_stack.h intern/builder/deg_builder_transitive.h intern/builder/pipeline.h intern/builder/pipeline_all_objects.h diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 3eeab23823c..0d4f9103149 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -586,11 +586,12 @@ void DepsgraphRelationBuilder::build_id(ID *id) void DepsgraphRelationBuilder::build_generic_id(ID *id) { - if (built_map_.checkIsBuiltAndTag(id)) { return; } + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*id); + build_idproperties(id->properties); build_animdata(id); build_parameters(id); @@ -621,6 +622,9 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll * recurses into all the nested objects and collections. */ return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(collection->id); + const bool group_done = built_map_.checkIsBuiltAndTag(collection); OperationKey object_transform_final_key(object != nullptr ? &object->id : nullptr, NodeType::TRANSFORM, @@ -684,6 +688,9 @@ void DepsgraphRelationBuilder::build_object(Object *object) if (built_map_.checkIsBuiltAndTag(object)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(object->id); + /* Object Transforms */ OperationCode base_op = (object->parent) ? OperationCode::TRANSFORM_PARENT : OperationCode::TRANSFORM_LOCAL; @@ -1133,6 +1140,9 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, if (cti == nullptr) { continue; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*con); + /* Special case for camera tracking -- it doesn't use targets to * define relations. */ /* TODO: we can now represent dependencies in a much richer manner, @@ -1500,6 +1510,9 @@ void DepsgraphRelationBuilder::build_action(bAction *action) if (built_map_.checkIsBuiltAndTag(action)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(action->id); + build_idproperties(action->id.properties); if (!BLI_listbase_is_empty(&action->curves)) { TimeSourceKey time_src_key; @@ -1787,6 +1800,9 @@ void DepsgraphRelationBuilder::build_world(World *world) if (built_map_.checkIsBuiltAndTag(world)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(world->id); + build_idproperties(world->id.properties); /* animation */ build_animdata(&world->id); @@ -2012,6 +2028,9 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) if (built_map_.checkIsBuiltAndTag(part)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(part->id); + /* Animation data relations. */ build_animdata(&part->id); build_parameters(&part->id); @@ -2070,6 +2089,9 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key) if (built_map_.checkIsBuiltAndTag(key)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(key->id); + build_idproperties(key->id.properties); /* Attach animdata to geometry. */ build_animdata(&key->id); @@ -2131,6 +2153,8 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->updateDepsgraph) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*md); + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx); @@ -2251,6 +2275,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) if (built_map_.checkIsBuiltAndTag(obdata)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*obdata); + build_idproperties(obdata->properties); /* Animation. */ build_animdata(obdata); @@ -2369,6 +2396,9 @@ void DepsgraphRelationBuilder::build_armature(bArmature *armature) if (built_map_.checkIsBuiltAndTag(armature)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(armature->id); + build_idproperties(armature->id.properties); build_animdata(&armature->id); build_parameters(&armature->id); @@ -2388,6 +2418,9 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) if (built_map_.checkIsBuiltAndTag(camera)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(camera->id); + build_idproperties(camera->id.properties); build_animdata(&camera->id); build_parameters(&camera->id); @@ -2405,6 +2438,9 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) if (built_map_.checkIsBuiltAndTag(lamp)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(lamp->id); + build_idproperties(lamp->id.properties); build_animdata(&lamp->id); build_parameters(&lamp->id); @@ -2469,6 +2505,9 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) if (built_map_.checkIsBuiltAndTag(ntree)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(ntree->id); + build_idproperties(ntree->id.properties); build_animdata(&ntree->id); build_parameters(&ntree->id); @@ -2574,6 +2613,9 @@ void DepsgraphRelationBuilder::build_material(Material *material) if (built_map_.checkIsBuiltAndTag(material)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(material->id); + build_idproperties(material->id.properties); /* animation */ build_animdata(&material->id); @@ -2610,6 +2652,9 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) if (built_map_.checkIsBuiltAndTag(texture)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(texture->id); + /* texture itself */ ComponentKey texture_key(&texture->id, NodeType::GENERIC_DATABLOCK); build_idproperties(texture->id.properties); @@ -2651,6 +2696,9 @@ void DepsgraphRelationBuilder::build_image(Image *image) if (built_map_.checkIsBuiltAndTag(image)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(image->id); + build_idproperties(image->id.properties); build_parameters(&image->id); } @@ -2660,6 +2708,9 @@ void DepsgraphRelationBuilder::build_cachefile(CacheFile *cache_file) if (built_map_.checkIsBuiltAndTag(cache_file)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(cache_file->id); + build_idproperties(cache_file->id.properties); /* Animation. */ build_animdata(&cache_file->id); @@ -2689,6 +2740,9 @@ void DepsgraphRelationBuilder::build_mask(Mask *mask) if (built_map_.checkIsBuiltAndTag(mask)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(mask->id); + ID *mask_id = &mask->id; build_idproperties(mask_id->properties); /* F-Curve animation. */ @@ -2727,6 +2781,8 @@ void DepsgraphRelationBuilder::build_freestyle_linestyle(FreestyleLineStyle *lin return; } + const BuilderStack::ScopedEntry stack_entry = stack_.trace(linestyle->id); + ID *linestyle_id = &linestyle->id; build_parameters(linestyle_id); build_idproperties(linestyle_id->properties); @@ -2739,6 +2795,9 @@ void DepsgraphRelationBuilder::build_movieclip(MovieClip *clip) if (built_map_.checkIsBuiltAndTag(clip)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(clip->id); + /* Animation. */ build_idproperties(clip->id.properties); build_animdata(&clip->id); @@ -2750,6 +2809,9 @@ void DepsgraphRelationBuilder::build_lightprobe(LightProbe *probe) if (built_map_.checkIsBuiltAndTag(probe)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(probe->id); + build_idproperties(probe->id.properties); build_animdata(&probe->id); build_parameters(&probe->id); @@ -2760,6 +2822,9 @@ void DepsgraphRelationBuilder::build_speaker(Speaker *speaker) if (built_map_.checkIsBuiltAndTag(speaker)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(speaker->id); + build_idproperties(speaker->id.properties); build_animdata(&speaker->id); build_parameters(&speaker->id); @@ -2776,6 +2841,9 @@ void DepsgraphRelationBuilder::build_sound(bSound *sound) if (built_map_.checkIsBuiltAndTag(sound)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(sound->id); + build_idproperties(sound->id.properties); build_animdata(&sound->id); build_parameters(&sound->id); @@ -2786,6 +2854,9 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation) if (built_map_.checkIsBuiltAndTag(simulation)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(simulation->id); + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); @@ -2850,6 +2921,9 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_SCENE_SEQUENCER)) { return; } + + /* TODO(sergey): Trace as a scene sequencer. */ + build_scene_audio(scene); ComponentKey scene_audio_key(&scene->id, NodeType::AUDIO); /* Make sure dependencies from sequences data goes to the sequencer evaluation. */ @@ -2893,6 +2967,9 @@ void DepsgraphRelationBuilder::build_vfont(VFont *vfont) if (built_map_.checkIsBuiltAndTag(vfont)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(vfont->id); + build_parameters(&vfont->id); build_idproperties(vfont->id.properties); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 1ccecc9a3f2..64bdd2334d8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -23,6 +23,7 @@ #include "intern/builder/deg_builder.h" #include "intern/builder/deg_builder_map.h" #include "intern/builder/deg_builder_rna.h" +#include "intern/builder/deg_builder_stack.h" #include "intern/depsgraph.h" #include "intern/node/deg_node.h" #include "intern/node/deg_node_component.h" @@ -363,6 +364,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { BuilderMap built_map_; RNANodeQuery rna_node_query_; + BuilderStack stack_; }; struct DepsNodeHandle { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h index 5cbd8c8dd75..aba4a011e72 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h @@ -9,6 +9,8 @@ #include "intern/node/deg_node_id.h" +#include <iostream> + #include "DNA_ID.h" #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" @@ -33,37 +35,30 @@ Relation *DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from, Node *node_to = get_node(key_to); OperationNode *op_from = node_from ? node_from->get_exit_operation() : nullptr; OperationNode *op_to = node_to ? node_to->get_entry_operation() : nullptr; + if (op_from && op_to) { return add_operation_relation(op_from, op_to, description, flags); } - else { - if (!op_from) { - /* XXX TODO: handle as error or report if needed. */ - fprintf(stderr, - "add_relation(%s) - Could not find op_from (%s)\n", - description, - key_from.identifier().c_str()); - } - else { - fprintf(stderr, - "add_relation(%s) - Failed, but op_from (%s) was ok\n", - description, - key_from.identifier().c_str()); - } - if (!op_to) { - /* XXX TODO: handle as error or report if needed. */ - fprintf(stderr, - "add_relation(%s) - Could not find op_to (%s)\n", - description, - key_to.identifier().c_str()); - } - else { - fprintf(stderr, - "add_relation(%s) - Failed, but op_to (%s) was ok\n", - description, - key_to.identifier().c_str()); - } + + /* TODO(sergey): Report error in the interface. */ + + std::cerr << "--------------------------------------------------------------------\n"; + std::cerr << "Failed to add relation \"" << description << "\"\n"; + + if (!op_from) { + std::cerr << "Could not find op_from: " << key_from.identifier() << "\n"; + } + + if (!op_to) { + std::cerr << "Could not find op_to: " << key_to.identifier() << "\n"; } + + if (!stack_.is_empty()) { + std::cerr << "\nTrace:\n\n"; + stack_.print_backtrace(std::cerr); + std::cerr << "\n"; + } + return nullptr; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index 65cf0e7d9df..2e491cd37a6 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -322,7 +322,11 @@ void DepsgraphRelationBuilder::build_rig(Object *object) RootPChanMap root_map; bool pose_depends_on_local_transform = false; LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*pchan); + LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*con); + switch (con->type) { case CONSTRAINT_TYPE_KINEMATIC: build_ik_pose(object, pchan, con, &root_map); @@ -356,6 +360,8 @@ void DepsgraphRelationBuilder::build_rig(Object *object) } /* Links between operations for each bone. */ LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*pchan); + build_idproperties(pchan->prop); OperationKey bone_local_key( &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc index cdb7361afc0..cd1917cb607 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc @@ -36,6 +36,9 @@ void DepsgraphRelationBuilder::build_scene_parameters(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_PARAMETERS)) { return; } + + /* TODO(sergey): Trace as a scene parameters. */ + build_idproperties(scene->id.properties); build_parameters(&scene->id); OperationKey parameters_eval_key( @@ -56,6 +59,9 @@ void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene) if (scene->nodetree == nullptr) { return; } + + /* TODO(sergey): Trace as a scene compositor. */ + build_nodetree(scene->nodetree); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_stack.cc b/source/blender/depsgraph/intern/builder/deg_builder_stack.cc new file mode 100644 index 00000000000..de0a5198a8a --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_stack.cc @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup depsgraph + */ + +#include "intern/builder/deg_builder_stack.h" + +#include <iomanip> +#include <ios> +#include <iostream> + +#include "BKE_idtype.h" + +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" + +namespace blender::deg { + +/* Spacing between adjacent columns, in number of spaces. */ +constexpr int kColumnSpacing = 4; + +/* Width of table columns including column padding. + * The type column width is a guesstimate based on "Particle Settings" with some extra padding. */ +constexpr int kPrintDepthWidth = 5 + kColumnSpacing; +constexpr int kPrintTypeWidth = 21 + kColumnSpacing; + +namespace { + +/* NOTE: Depth column printing is already taken care of. */ + +void print(std::ostream &stream, const ID *id) +{ + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id); + stream << std::setw(kPrintTypeWidth) << id_type_info->name << (id->name + 2) << "\n"; +} + +void print(std::ostream &stream, const bConstraint *constraint) +{ + stream << std::setw(kPrintTypeWidth) << ("Constraint") << constraint->name << "\n"; +} + +void print(std::ostream &stream, const ModifierData *modifier_data) +{ + stream << std::setw(kPrintTypeWidth) << ("Modifier") << modifier_data->name << "\n"; +} + +void print(std::ostream &stream, const bPoseChannel *pchan) +{ + stream << std::setw(kPrintTypeWidth) << ("Pose Channel") << pchan->name << "\n"; +} + +} // namespace + +void BuilderStack::print_backtrace(std::ostream &stream) +{ + const std::ios_base::fmtflags old_flags(stream.flags()); + + stream << std::left; + + stream << std::setw(kPrintDepthWidth) << "Depth" << std::setw(kPrintTypeWidth) << "Type" + << "Name" + << "\n"; + + stream << std::setw(kPrintDepthWidth) << "-----" << std::setw(kPrintTypeWidth) << "----" + << "----" + << "\n"; + + int depth = 1; + for (const Entry &entry : stack_) { + stream << std::setw(kPrintDepthWidth) << depth; + ++depth; + + if (entry.id_ != nullptr) { + print(stream, entry.id_); + } + else if (entry.constraint_ != nullptr) { + print(stream, entry.constraint_); + } + else if (entry.modifier_data_ != nullptr) { + print(stream, entry.modifier_data_); + } + else if (entry.pchan_ != nullptr) { + print(stream, entry.pchan_); + } + } + + stream.flags(old_flags); +} + +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_stack.h b/source/blender/depsgraph/intern/builder/deg_builder_stack.h new file mode 100644 index 00000000000..3f9cc83928a --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_stack.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "BLI_utildefines.h" +#include "BLI_vector.hh" + +struct ID; +struct bConstraint; +struct bPoseChannel; +struct ModifierData; + +namespace blender::deg { + +/* This class keeps track of the builder calls nesting, allowing to unroll them back and provide a + * clue about how the builder made it to its current state. + * + * The tracing is based on the builder giving a trace clues to the stack. Typical usage is: + * + * void DepsgraphRelationBuilder::my_id_builder(ID *id) + * { + * if (built_map_.checkIsBuiltAndTag(id)) { + * return; + * } + * + * const BuilderStack::ScopedEntry stack_entry = stack_.trace(*id); + * + * ... + * } + */ +class BuilderStack { + public: + /* Entry of the backtrace. + * A cheap-to-construct wrapper which allows to gather a proper string representation whenever + * the stack is printed. */ + class Entry { + public: + explicit Entry(const ID &id) : id_(&id) + { + } + + explicit Entry(const bConstraint &constraint) : constraint_(&constraint) + { + } + + explicit Entry(const bPoseChannel &pchan) : pchan_(&pchan) + { + } + + explicit Entry(const ModifierData &modifier_data) : modifier_data_(&modifier_data) + { + } + + private: + friend class BuilderStack; + + const ID *id_ = nullptr; + const bConstraint *constraint_ = nullptr; + const ModifierData *modifier_data_ = nullptr; + const bPoseChannel *pchan_ = nullptr; + }; + + using Stack = Vector<Entry>; + + /* A helper class to provide a RAII style of tracing. It is constructed by the + * `BuilderStack::trace` (which pushes entry to the stack), and upon destruction of this object + * the corresponding entry is popped from the stack. + * + * The goal of this `ScopedEntry` is to free developers from worrying about removing entries from + * the stack whenever leaving a builder step scope. */ + class ScopedEntry { + public: + /* Delete copy constructor and operator: scoped entries are only supposed to be constructed + * once and never copied. */ + ScopedEntry(const ScopedEntry &other) = delete; + ScopedEntry &operator=(const ScopedEntry &other) = delete; + + /* Move semantic. */ + ScopedEntry(ScopedEntry &&other) noexcept : stack_(other.stack_) + { + other.stack_ = nullptr; + } + ScopedEntry &operator=(ScopedEntry &&other) + { + if (this == &other) { + return *this; + } + + stack_ = other.stack_; + other.stack_ = nullptr; + + return *this; + } + + ~ScopedEntry() + { + /* Stack will become nullptr when the entry was moved somewhere else. */ + if (stack_ != nullptr) { + BLI_assert(!stack_->is_empty()); + stack_->pop_last(); + } + } + + private: + friend BuilderStack; + + explicit ScopedEntry(Stack &stack) : stack_(&stack) + { + } + + Stack *stack_; + }; + + BuilderStack() = default; + ~BuilderStack() = default; + + bool is_empty() const + { + return stack_.is_empty(); + } + + void print_backtrace(std::ostream &stream); + + template<class... Args> ScopedEntry trace(const Args &...args) + { + stack_.append_as(args...); + + return ScopedEntry(stack_); + } + + private: + Stack stack_; +}; + +} // namespace blender::deg diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 3e90c2cb707..3381dbadbab 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/mesh_extractors/extract_mesh_vbo_uv.cc intern/mesh_extractors/extract_mesh_vbo_vcol.cc intern/mesh_extractors/extract_mesh_vbo_weights.cc + intern/draw_attributes.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_curves.cc intern/draw_cache_impl_displist.c @@ -198,6 +199,7 @@ set(SRC DRW_select_buffer.h intern/DRW_gpu_wrapper.hh intern/DRW_render.h + intern/draw_attributes.h intern/draw_cache.h intern/draw_cache_extract.h intern/draw_cache_impl.h diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 105600d2333..6fd5d97089d 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -169,7 +169,6 @@ extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_math_lib_glsl[]; extern char datatoc_common_math_geom_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; extern char datatoc_gpu_shader_codegen_lib_glsl[]; extern char datatoc_ambient_occlusion_lib_glsl[]; @@ -278,7 +277,6 @@ static void eevee_shader_library_ensure(void) DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib); - DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_codegen_lib); DRW_SHADER_LIB_ADD(e_data.lib, random_lib); DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib); diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc index 05577944140..216a15de2b9 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc +++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc @@ -118,6 +118,10 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, info.vertex_inputs_.clear(); } + if (is_hair) { + info.additional_info("draw_curves_infos"); + } + if (!is_volume) { info.define("EEVEE_GENERATED_INTERFACE"); info.vertex_out(*stage_interface); diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 5295a05b965..2926f8c5a89 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -65,6 +65,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf) } # endif +/* Per attribute scope follows loading order. */ +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = hairStrandID; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { /* Not supported. */ @@ -73,22 +89,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf) vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } #else diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 23324146b7e..8e1bafe8d92 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -108,9 +108,7 @@ GlobalData init_globals(void) # endif surf.barycentric_coords = vec2(0.0); surf.barycentric_dists = vec3(0.0); - if (!FrontFacing) { - surf.N = -surf.N; - } + surf.N = (FrontFacing) ? surf.N : -surf.N; # ifdef HAIR_SHADER vec3 V = cameraVec(surf.P); /* Shade as a cylinder. */ diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 7cb4d7ac25c..a8e95e13b12 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -72,6 +72,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf) } # endif +/* Per attribute scope follows loading order. */ +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = hairStrandID; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { return vec4(hairTangent, 1.0); @@ -79,22 +95,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf) vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } #else @@ -117,7 +133,7 @@ vec3 attr_load_orco(vec4 orco) vec4 attr_load_tangent(vec4 tangent) { - tangent.xyz = normal_object_to_world(tangent.xyz); + tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz)); return tangent; } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 009eb54864c..09aa97e49e9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -161,6 +161,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu } } info.vertex_inputs_.clear(); + info.additional_info("draw_curves_infos"); break; case MAT_GEOM_WORLD: /** diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl index 3c5acf62e30..1b113e529b6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl @@ -112,6 +112,7 @@ float attr_load_float(float attr) /** \name Curve * * Curve objects loads attributes from buffers through sampler buffers. + * Per attribute scope follows loading order. * \{ */ # ifdef OBINFO_LIB @@ -122,6 +123,22 @@ vec3 attr_load_orco(vec4 orco) return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; } # endif + +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = interp.curves_strand_id; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { /* Not supported for the moment. */ @@ -137,19 +154,19 @@ vec4 attr_load_color(samplerBuffer cd_buf) } vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } /** \} */ diff --git a/source/blender/draw/engines/overlay/overlay_shader_shared.h b/source/blender/draw/engines/overlay/overlay_shader_shared.h index 99700cdcec4..339b6f02e1a 100644 --- a/source/blender/draw/engines/overlay/overlay_shader_shared.h +++ b/source/blender/draw/engines/overlay/overlay_shader_shared.h @@ -24,19 +24,19 @@ typedef struct OVERLAY_GridData OVERLAY_GridData; #define BG_MASK 5 enum OVERLAY_GridBits { - SHOW_AXIS_X = (1 << 0), - SHOW_AXIS_Y = (1 << 1), - SHOW_AXIS_Z = (1 << 2), - SHOW_GRID = (1 << 3), - PLANE_XY = (1 << 4), - PLANE_XZ = (1 << 5), - PLANE_YZ = (1 << 6), - CLIP_ZPOS = (1 << 7), - CLIP_ZNEG = (1 << 8), - GRID_BACK = (1 << 9), - GRID_CAMERA = (1 << 10), - PLANE_IMAGE = (1 << 11), - CUSTOM_GRID = (1 << 12), + SHOW_AXIS_X = (1u << 0u), + SHOW_AXIS_Y = (1u << 1u), + SHOW_AXIS_Z = (1u << 2u), + SHOW_GRID = (1u << 3u), + PLANE_XY = (1u << 4u), + PLANE_XZ = (1u << 5u), + PLANE_YZ = (1u << 6u), + CLIP_ZPOS = (1u << 7u), + CLIP_ZNEG = (1u << 8u), + GRID_BACK = (1u << 9u), + GRID_CAMERA = (1u << 10u), + PLANE_IMAGE = (1u << 11u), + CUSTOM_GRID = (1u << 12u), }; /* Match: #SI_GRID_STEPS_LEN */ diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index 3e3c0e4e89f..472a589f441 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -36,7 +36,7 @@ bvec4 gather_edges(vec2 uv, uint ref) #ifdef GPU_ARB_texture_gather ids = textureGather(outlineId, uv); #else - vec3 ofs = vec3(0.5, 0.5, -0.5) * drw_view.viewport_size_inversey; + vec3 ofs = vec3(0.5, 0.5, -0.5) * drw_view.viewport_size_inverse.xyy; ids.x = textureLod(outlineId, uv - ofs.xz, 0.0).r; ids.y = textureLod(outlineId, uv + ofs.xy, 0.0).r; ids.z = textureLod(outlineId, uv + ofs.xz, 0.0).r; diff --git a/source/blender/draw/intern/draw_attributes.cc b/source/blender/draw/intern/draw_attributes.cc new file mode 100644 index 00000000000..714f1dbb3d1 --- /dev/null +++ b/source/blender/draw/intern/draw_attributes.cc @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#include "draw_attributes.h" + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool drw_attributes_has_request(const DRW_Attributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void drw_attributes_merge_requests(const DRW_Attributes *src_requests, + DRW_Attributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (drw_attributes_has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +void drw_attributes_clear(DRW_Attributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_Attributes)); +} + +void drw_attributes_merge(DRW_Attributes *dst, + const DRW_Attributes *src, + ThreadMutex *render_mutex) +{ + BLI_mutex_lock(render_mutex); + drw_attributes_merge_requests(src, dst); + BLI_mutex_unlock(render_mutex); +} + +bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b) +{ + for (int i = 0; i < b->num_requests; i++) { + if (!drw_attributes_has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return nullptr; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; + return req; +} + +bool drw_custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[7] = { + CD_PROP_BOOL, + CD_PROP_INT8, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} diff --git a/source/blender/draw/intern/draw_attributes.h b/source/blender/draw/intern/draw_attributes.h new file mode 100644 index 00000000000..192ffa43337 --- /dev/null +++ b/source/blender/draw/intern/draw_attributes.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + * + * \brief Utilities for rendering attributes. + */ + +#pragma once + +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute.h" + +#include "BLI_sys_types.h" +#include "BLI_threads.h" + +#include "GPU_shader.h" +#include "GPU_vertex_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; + char attribute_name[64]; +} DRW_AttributeRequest; + +typedef struct DRW_Attributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_Attributes; + +void drw_attributes_clear(DRW_Attributes *attributes); + +void drw_attributes_merge(DRW_Attributes *dst, + const DRW_Attributes *src, + ThreadMutex *render_mutex); + +/* Return true if all requests in b are in a. */ +bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b); + +DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain); + +bool drw_custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 67700a4274f..fb074cc728e 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -3400,6 +3400,9 @@ void DRW_batch_cache_free_old(Object *ob, int ctime) case OB_MESH: DRW_mesh_batch_cache_free_old((Mesh *)ob->data, ctime); break; + case OB_CURVES: + DRW_curves_batch_cache_free_old((Curves *)ob->data, ctime); + break; /* TODO: all cases. */ default: break; diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index cb6006e303a..ce3ad9923da 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -20,6 +20,8 @@ struct TaskGraph; #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" +#include "draw_attributes.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -67,17 +69,6 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) -typedef struct DRW_AttributeRequest { - CustomDataType cd_type; - int layer_index; - AttributeDomain domain; -} DRW_AttributeRequest; - -typedef struct DRW_MeshAttributes { - DRW_AttributeRequest requests[GPU_MAX_ATTR]; - int num_requests; -} DRW_MeshAttributes; - typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -294,7 +285,7 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; - DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + DRW_Attributes attr_used, attr_needed, attr_used_over_time; int lastmatch; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index ec544d8e786..3d44d3d1b3f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -793,7 +793,12 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, /* The order in which extractors are added to the list matters somewhat, as some buffers are * reused when building others. */ EXTRACT_ADD_REQUESTED(ibo, tris); - EXTRACT_ADD_REQUESTED(vbo, pos_nor); + + /* Orcos are extracted at the same time as positions. */ + if (DRW_vbo_requested(mbuflist->vbo.pos_nor) || DRW_vbo_requested(mbuflist->vbo.orco)) { + extractors.append(&extract_pos_nor); + } + EXTRACT_ADD_REQUESTED(vbo, lnor); for (int i = 0; i < GPU_MAX_ATTR; i++) { EXTRACT_ADD_REQUESTED(vbo, attr[i]); @@ -843,7 +848,6 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle); EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask); EXTRACT_ADD_REQUESTED(ibo, lines_adjacency); - EXTRACT_ADD_REQUESTED(vbo, orco); EXTRACT_ADD_REQUESTED(vbo, vcol); EXTRACT_ADD_REQUESTED(vbo, weights); EXTRACT_ADD_REQUESTED(vbo, sculpt_data); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index f877c94208f..0755d5967d5 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -83,6 +83,7 @@ void DRW_batch_cache_free_old(struct Object *ob, int ctime); * \note For now this only free the shading batches / VBO if any cd layers is not needed anymore. */ void DRW_mesh_batch_cache_free_old(struct Mesh *me, int ctime); +void DRW_curves_batch_cache_free_old(struct Curves *curves, int ctime); /** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index 1896df7c650..f9cf0021fcd 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -24,6 +24,7 @@ #include "DNA_scene_types.h" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "GPU_batch.h" #include "GPU_material.h" @@ -31,10 +32,13 @@ #include "DRW_render.h" +#include "draw_attributes.h" #include "draw_cache_impl.h" /* own include */ #include "draw_cache_inline.h" #include "draw_curves_private.h" /* own include */ +#include "draw_shader.h" +using blender::ColorGeometry4f; using blender::float3; using blender::IndexRange; using blender::MutableSpan; @@ -50,6 +54,10 @@ struct CurvesBatchCache { /* To determine if cache is invalid. */ bool is_dirty; + + /** Needed when updating material data (e.g. attributes) as the same curves might be used for + * multiple objects with different materials. */ + ThreadMutex render_mutex; }; static bool curves_batch_cache_valid(const Curves &curves) @@ -64,6 +72,7 @@ static void curves_batch_cache_init(Curves &curves) if (!cache) { cache = MEM_cnew<CurvesBatchCache>(__func__); + BLI_mutex_init(&cache->render_mutex); curves.batch_cache = cache; } else { @@ -73,6 +82,23 @@ static void curves_batch_cache_init(Curves &curves) cache->is_dirty = false; } +static void curves_discard_attributes(CurvesEvalCache &curves_cache) +{ + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_attributes_buf[i]); + DRW_TEXTURE_FREE_SAFE(curves_cache.proc_attributes_tex[i]); + } + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + for (int j = 0; j < GPU_MAX_ATTR; j++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.final[i].attributes_buf[j]); + DRW_TEXTURE_FREE_SAFE(curves_cache.final[i].attributes_tex[j]); + } + + drw_attributes_clear(&curves_cache.final[i].attr_used); + } +} + static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) { /* TODO: more granular update tagging. */ @@ -93,6 +119,8 @@ static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) GPU_BATCH_DISCARD_SAFE(curves_cache.final[i].proc_hairs[j]); } } + + curves_discard_attributes(curves_cache); } static void curves_batch_cache_clear(Curves &curves) @@ -139,9 +167,39 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) void DRW_curves_batch_cache_free(Curves *curves) { curves_batch_cache_clear(*curves); + CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); + BLI_mutex_end(&cache->render_mutex); MEM_SAFE_FREE(curves->batch_cache); } +void DRW_curves_batch_cache_free_old(Curves *curves, int ctime) +{ + CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); + if (cache == nullptr) { + return; + } + + bool do_discard = false; + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + CurvesEvalFinalCache &final_cache = cache->curves_cache.final[i]; + + if (drw_attributes_overlap(&final_cache.attr_used_over_time, &final_cache.attr_used)) { + final_cache.last_attr_matching_time = ctime; + } + + if (ctime - final_cache.last_attr_matching_time > U.vbotimeout) { + do_discard = true; + } + + drw_attributes_clear(&final_cache.attr_used_over_time); + } + + if (do_discard) { + curves_discard_attributes(cache->curves_cache); + } +} + static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_cache) { if (curves_cache.proc_point_buf != nullptr) { @@ -242,6 +300,88 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, } } +void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]) +{ + char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(r_sampler_name, 32, "a%s", attr_safe_name); +} + +static void curves_batch_cache_ensure_procedural_final_attr( + CurvesEvalCache &cache, GPUVertFormat *format, int subdiv, int index, const char *name) +{ + CurvesEvalFinalCache &final_cache = cache.final[subdiv]; + final_cache.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(format, + GPU_USAGE_DEVICE_ONLY); + + /* Create a destination buffer for the transform feedback. Sized appropriately */ + /* Those are points! not line segments. */ + GPU_vertbuf_data_alloc(final_cache.attributes_buf[index], + final_cache.strands_res * cache.strands_len); + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(final_cache.attributes_buf[index]); + + final_cache.attributes_tex[index] = GPU_texture_create_from_vertbuf( + name, final_cache.attributes_buf[index]); +} + +static void curves_batch_ensure_attribute(const Curves &curves, + CurvesEvalCache &cache, + const DRW_AttributeRequest &request, + int subdiv, + int index) +{ + GPU_VERTBUF_DISCARD_SAFE(cache.proc_attributes_buf[index]); + DRW_TEXTURE_FREE_SAFE(cache.proc_attributes_tex[index]); + + char sampler_name[32]; + drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + /* All attributes use vec4, see comment below. */ + GPU_vertformat_attr_add(&format, sampler_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + cache.proc_attributes_buf[index] = GPU_vertbuf_create_with_format(&format); + GPUVertBuf *attr_vbo = cache.proc_attributes_buf[index]; + + GPU_vertbuf_data_alloc(attr_vbo, + request.domain == ATTR_DOMAIN_POINT ? curves.geometry.point_num : + curves.geometry.curve_num); + + CurveComponent component; + component.replace(const_cast<Curves *>(&curves), GeometryOwnershipType::ReadOnly); + + /* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done + * by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following + * the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a + * similar texture state swizzle to map the attribute correctly as for volume attributes, so we + * can control the conversion ourselves. */ + blender::VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>( + request.attribute_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f}); + + MutableSpan<ColorGeometry4f> vbo_span{ + static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(attr_vbo)), + component.attribute_domain_num(request.domain)}; + + attribute.materialize(vbo_span); + + GPU_vertbuf_use(attr_vbo); + cache.proc_attributes_tex[index] = GPU_texture_create_from_vertbuf(sampler_name, attr_vbo); + + /* Existing final data may have been for a different attribute (with a different name or domain), + * free the data. */ + GPU_VERTBUF_DISCARD_SAFE(cache.final[subdiv].attributes_buf[index]); + DRW_TEXTURE_FREE_SAFE(cache.final[subdiv].attributes_tex[index]); + + /* Ensure final data for points. */ + if (request.domain == ATTR_DOMAIN_POINT) { + curves_batch_cache_ensure_procedural_final_attr(cache, &format, subdiv, index, sampler_name); + } +} + static void curves_batch_cache_fill_strands_data(const Curves &curves_id, GPUVertBufRaw &data_step, GPUVertBufRaw &seg_step) @@ -358,6 +498,88 @@ static void curves_batch_cache_ensure_procedural_indices(Curves &curves, prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); } +static bool curves_ensure_attributes(const Curves &curves, + CurvesBatchCache &cache, + GPUMaterial *gpu_material, + int subdiv) +{ + ThreadMutex *render_mutex = &cache.render_mutex; + const CustomData *cd_curve = &curves.geometry.curve_data; + const CustomData *cd_point = &curves.geometry.point_data; + + DRW_Attributes attrs_needed; + drw_attributes_clear(&attrs_needed); + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + const char *name = gpu_attr->name; + int type = gpu_attr->type; + int layer = -1; + AttributeDomain domain; + + if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) { + domain = ATTR_DOMAIN_CURVE; + } + else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else { + continue; + } + + switch (type) { + default: + break; + case CD_PROP_BOOL: + case CD_PROP_INT8: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1) { + DRW_AttributeRequest *req = drw_attributes_add_request( + &attrs_needed, (CustomDataType)type, layer, domain); + if (req) { + BLI_strncpy(req->attribute_name, name, sizeof(req->attribute_name)); + } + } + break; + } + } + } + + CurvesEvalFinalCache &final_cache = cache.curves_cache.final[subdiv]; + + const bool attr_overlap = drw_attributes_overlap(&final_cache.attr_used, &attrs_needed); + if (attr_overlap == false) { + /* Some new attributes have been added, free all and start over. */ + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(cache.curves_cache.proc_attributes_buf[i]); + DRW_TEXTURE_FREE_SAFE(cache.curves_cache.proc_attributes_tex[i]); + } + drw_attributes_merge(&final_cache.attr_used, &attrs_needed, render_mutex); + } + drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, render_mutex); + + bool need_tf_update = false; + + for (int i = 0; i < final_cache.attr_used.num_requests; i++) { + const DRW_AttributeRequest &request = final_cache.attr_used.requests[i]; + + if (cache.curves_cache.proc_attributes_buf[i] != nullptr) { + continue; + } + + if (request.domain == ATTR_DOMAIN_POINT) { + need_tf_update = true; + } + + curves_batch_ensure_attribute(curves, cache.curves_cache, request, subdiv, i); + } + + return need_tf_update; +} + bool curves_ensure_procedural_data(Object *object, CurvesEvalCache **r_hair_cache, GPUMaterial *gpu_material, @@ -395,6 +617,10 @@ bool curves_ensure_procedural_data(Object *object, curves, cache.curves_cache, thickness_res, subdiv); } + if (gpu_material) { + need_ft_update |= curves_ensure_attributes(curves, cache, gpu_material, subdiv); + } + return need_ft_update; } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 7fdeaf34965..a6ab2176d16 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -281,91 +281,6 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } -/** \name DRW_MeshAttributes - * - * Utilities for handling requested attributes. - * \{ */ - -/* Return true if the given DRW_AttributeRequest is already in the requests. */ -static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) -{ - for (int i = 0; i < requests->num_requests; i++) { - const DRW_AttributeRequest src_req = requests->requests[i]; - if (src_req.domain != req.domain) { - continue; - } - if (src_req.layer_index != req.layer_index) { - continue; - } - if (src_req.cd_type != req.cd_type) { - continue; - } - return true; - } - return false; -} - -static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, - DRW_MeshAttributes *dst_requests) -{ - for (int i = 0; i < src_requests->num_requests; i++) { - if (dst_requests->num_requests == GPU_MAX_ATTR) { - return; - } - - if (has_request(dst_requests, src_requests->requests[i])) { - continue; - } - - dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; - dst_requests->num_requests += 1; - } -} - -static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) -{ - memset(attributes, 0, sizeof(DRW_MeshAttributes)); -} - -static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, - const DRW_MeshAttributes *src, - ThreadMutex *mesh_render_mutex) -{ - BLI_mutex_lock(mesh_render_mutex); - mesh_attrs_merge_requests(src, dst); - BLI_mutex_unlock(mesh_render_mutex); -} - -/* Return true if all requests in b are in a. */ -static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) -{ - for (int i = 0; i < b->num_requests; i++) { - if (!has_request(a, b->requests[i])) { - return false; - } - } - - return true; -} - -static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, - CustomDataType type, - int layer, - AttributeDomain domain) -{ - if (attrs->num_requests >= GPU_MAX_ATTR) { - return; - } - - DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; - req->cd_type = type; - req->layer_index = layer; - req->domain = domain; - attrs->num_requests += 1; -} - -/** \} */ - BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -475,36 +390,6 @@ static void mesh_cd_calc_active_mloopcol_layer(const Object *object, } } -static bool custom_data_match_attribute(const CustomData *custom_data, - const char *name, - int *r_layer_index, - int *r_type) -{ - const int possible_attribute_types[7] = { - CD_PROP_BOOL, - CD_PROP_INT8, - CD_PROP_INT32, - CD_PROP_FLOAT, - CD_PROP_FLOAT2, - CD_PROP_FLOAT3, - CD_PROP_COLOR, - }; - - for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { - const int attr_type = possible_attribute_types[i]; - int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); - if (layer_index == -1) { - continue; - } - - *r_layer_index = layer_index; - *r_type = attr_type; - return true; - } - - return false; -} - static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, const CustomData *cd_vdata, const CustomData *cd_ldata, @@ -556,7 +441,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, const Mesh *me, struct GPUMaterial **gpumat_array, int gpumat_array_len, - DRW_MeshAttributes *attributes) + DRW_Attributes *attributes) { const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); @@ -636,16 +521,16 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, if (layer == -1) { /* Try to match a generic attribute, we use the first attribute domain with a * matching name. */ - if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { domain = ATTR_DOMAIN_POINT; } - else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { domain = ATTR_DOMAIN_CORNER; } - else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { domain = ATTR_DOMAIN_FACE; } - else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { domain = ATTR_DOMAIN_EDGE; } else { @@ -718,7 +603,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, } if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); + drw_attributes_add_request(attributes, type, layer, domain); } break; } @@ -729,7 +614,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, case CD_PROP_FLOAT: case CD_PROP_FLOAT2: { if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); + drw_attributes_add_request(attributes, type, layer, domain); } break; } @@ -1317,8 +1202,8 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshAttributes attrs_needed; - drw_mesh_attributes_clear(&attrs_needed); + DRW_Attributes attrs_needed; + drw_attributes_clear(&attrs_needed); DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( object, me, gpumat_array, gpumat_array_len, &attrs_needed); @@ -1326,7 +1211,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1596,7 +1481,7 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } - if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + if (drw_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { cache->lastmatch = ctime; } @@ -1605,12 +1490,12 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) } mesh_cd_layers_type_clear(&cache->cd_used_over_time); - drw_mesh_attributes_clear(&cache->attr_used_over_time); + drw_attributes_clear(&cache->attr_used_over_time); } static void drw_add_attributes_vbo(GPUBatch *batch, MeshBufferList *mbuflist, - DRW_MeshAttributes *attr_used) + DRW_Attributes *attr_used) { for (int i = 0; i < attr_used->num_requests; i++) { DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); @@ -1721,7 +1606,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + bool attr_overlap = drw_attributes_overlap(&cache->attr_used, &cache->attr_needed); if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { @@ -1741,7 +1626,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } - if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + if (!drw_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { for (int i = 0; i < GPU_MAX_ATTR; i++) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); } @@ -1756,13 +1641,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); - drw_mesh_attributes_clear(&cache->attr_needed); + drw_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index cfdf059ca37..a0c7e064e00 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -69,6 +69,8 @@ enum { SHADER_PATCH_EVALUATION, SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS, + SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS, + SHADER_PATCH_EVALUATION_ORCO, SHADER_COMP_CUSTOM_DATA_INTERP_1D, SHADER_COMP_CUSTOM_DATA_INTERP_2D, SHADER_COMP_CUSTOM_DATA_INTERP_3D, @@ -107,7 +109,9 @@ static const char *get_shader_code(int shader_type) } case SHADER_PATCH_EVALUATION: case SHADER_PATCH_EVALUATION_FVAR: - case SHADER_PATCH_EVALUATION_FACE_DOTS: { + case SHADER_PATCH_EVALUATION_FACE_DOTS: + case SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS: + case SHADER_PATCH_EVALUATION_ORCO: { return datatoc_common_subdiv_patch_evaluation_comp_glsl; } case SHADER_COMP_CUSTOM_DATA_INTERP_1D: @@ -163,6 +167,12 @@ static const char *get_shader_name(int shader_type) case SHADER_PATCH_EVALUATION_FACE_DOTS: { return "subdiv patch evaluation face dots"; } + case SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS: { + return "subdiv patch evaluation face dots with normals"; + } + case SHADER_PATCH_EVALUATION_ORCO: { + return "subdiv patch evaluation orco"; + } case SHADER_COMP_CUSTOM_DATA_INTERP_1D: { return "subdiv custom data interp 1D"; } @@ -206,6 +216,19 @@ static GPUShader *get_patch_evaluation_shader(int shader_type) "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" "#define FDOTS_EVALUATION\n"; } + else if (shader_type == SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS) { + defines = + "#define OSD_PATCH_BASIS_GLSL\n" + "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" + "#define FDOTS_EVALUATION\n" + "#define FOTS_NORMALS\n"; + } + else if (shader_type == SHADER_PATCH_EVALUATION_ORCO) { + defines = + "#define OSD_PATCH_BASIS_GLSL\n" + "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" + "#define ORCO_EVALUATION\n"; + } else { defines = "#define OSD_PATCH_BASIS_GLSL\n" @@ -236,7 +259,8 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines) if (ELEM(shader_type, SHADER_PATCH_EVALUATION, SHADER_PATCH_EVALUATION_FVAR, - SHADER_PATCH_EVALUATION_FACE_DOTS)) { + SHADER_PATCH_EVALUATION_FACE_DOTS, + SHADER_PATCH_EVALUATION_ORCO)) { return get_patch_evaluation_shader(shader_type); } if (g_subdiv_shaders[shader_type] == nullptr) { @@ -588,8 +612,9 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache) #define SUBDIV_COARSE_FACE_FLAG_SMOOTH 1u #define SUBDIV_COARSE_FACE_FLAG_SELECT 2u #define SUBDIV_COARSE_FACE_FLAG_ACTIVE 4u +#define SUBDIV_COARSE_FACE_FLAG_HIDDEN 8u -#define SUBDIV_COARSE_FACE_FLAG_OFFSET 29u +#define SUBDIV_COARSE_FACE_FLAG_OFFSET 28u #define SUBDIV_COARSE_FACE_FLAG_SMOOTH_MASK \ (SUBDIV_COARSE_FACE_FLAG_SMOOTH << SUBDIV_COARSE_FACE_FLAG_OFFSET) @@ -597,10 +622,12 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache) (SUBDIV_COARSE_FACE_FLAG_SELECT << SUBDIV_COARSE_FACE_FLAG_OFFSET) #define SUBDIV_COARSE_FACE_FLAG_ACTIVE_MASK \ (SUBDIV_COARSE_FACE_FLAG_ACTIVE << SUBDIV_COARSE_FACE_FLAG_OFFSET) +#define SUBDIV_COARSE_FACE_FLAG_HIDDEN_MASK \ + (SUBDIV_COARSE_FACE_FLAG_HIDDEN << SUBDIV_COARSE_FACE_FLAG_OFFSET) #define SUBDIV_COARSE_FACE_LOOP_START_MASK \ ~((SUBDIV_COARSE_FACE_FLAG_SMOOTH | SUBDIV_COARSE_FACE_FLAG_SELECT | \ - SUBDIV_COARSE_FACE_FLAG_ACTIVE) \ + SUBDIV_COARSE_FACE_FLAG_ACTIVE | SUBDIV_COARSE_FACE_FLAG_HIDDEN) \ << SUBDIV_COARSE_FACE_FLAG_OFFSET) static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act) @@ -617,6 +644,9 @@ static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act) if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; } + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + flag |= SUBDIV_COARSE_FACE_FLAG_HIDDEN; + } if (f == efa_act) { flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; } @@ -647,6 +677,9 @@ static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t * if ((mesh->mpoly[i].flag & ME_FACE_SEL) != 0) { flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; } + if ((mesh->mpoly[i].flag & ME_HIDE) != 0) { + flag |= SUBDIV_COARSE_FACE_FLAG_HIDDEN; + } flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); } } @@ -710,6 +743,23 @@ static DRWSubdivCache *mesh_batch_cache_ensure_subdiv_cache(MeshBatchCache *mbc) return subdiv_cache; } +static void draw_subdiv_invalidate_evaluator_for_orco(Subdiv *subdiv, Mesh *mesh) +{ + const bool has_orco = CustomData_has_layer(&mesh->vdata, CD_ORCO); + if (has_orco && subdiv->evaluator && !subdiv->evaluator->hasVertexData(subdiv->evaluator)) { + /* If we suddenly have/need original coordinates, recreate the evaluator if the extra + * source was not created yet. The refiner also has to be recreated as refinement for source + * and vertex data is done only once. */ + openSubdiv_deleteEvaluator(subdiv->evaluator); + subdiv->evaluator = nullptr; + + if (subdiv->topology_refiner != nullptr) { + openSubdiv_deleteTopologyRefiner(subdiv->topology_refiner); + subdiv->topology_refiner = nullptr; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1119,12 +1169,17 @@ struct DRWSubdivUboStorage { uint coarse_face_select_mask; uint coarse_face_smooth_mask; uint coarse_face_active_mask; + uint coarse_face_hidden_mask; uint coarse_face_loopstart_mask; /* Number of elements to process in the compute shader (can be the coarse quad count, or the * final vertex count, depending on which compute pass we do). This is used to early out in case * of out of bond accesses as compute dispatch are of fixed size. */ uint total_dispatch_size; + + int _pad0; + int _pad2; + int _pad3; }; static_assert((sizeof(DRWSubdivUboStorage) % 16) == 0, @@ -1151,6 +1206,7 @@ static void draw_subdiv_init_ubo_storage(const DRWSubdivCache *cache, ubo->coarse_face_smooth_mask = SUBDIV_COARSE_FACE_FLAG_SMOOTH_MASK; ubo->coarse_face_select_mask = SUBDIV_COARSE_FACE_FLAG_SELECT_MASK; ubo->coarse_face_active_mask = SUBDIV_COARSE_FACE_FLAG_ACTIVE_MASK; + ubo->coarse_face_hidden_mask = SUBDIV_COARSE_FACE_FLAG_HIDDEN_MASK; ubo->coarse_face_loopstart_mask = SUBDIV_COARSE_FACE_LOOP_START_MASK; ubo->total_dispatch_size = total_dispatch_size; } @@ -1230,7 +1286,9 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache, GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1); } -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_nor) +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, + GPUVertBuf *pos_nor, + GPUVertBuf *orco) { if (!draw_subdiv_cache_need_polygon_data(cache)) { /* Happens on meshes with only loose geometry. */ @@ -1245,6 +1303,14 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no get_subdiv_vertex_format()); evaluator->wrapSrcBuffer(evaluator, &src_buffer_interface); + GPUVertBuf *src_extra_buffer = nullptr; + if (orco) { + OpenSubdiv_Buffer src_extra_buffer_interface; + src_extra_buffer = create_buffer_and_interface(&src_extra_buffer_interface, + get_subdiv_vertex_format()); + evaluator->wrapSrcVertexDataBuffer(evaluator, &src_extra_buffer_interface); + } + OpenSubdiv_Buffer patch_arrays_buffer_interface; GPUVertBuf *patch_arrays_buffer = create_buffer_and_interface(&patch_arrays_buffer_interface, get_patch_array_format()); @@ -1260,7 +1326,8 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no get_patch_param_format()); evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface); - GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION); + GPUShader *shader = get_patch_evaluation_shader(orco ? SHADER_PATCH_EVALUATION_ORCO : + SHADER_PATCH_EVALUATION); GPU_shader_bind(shader); int binding_point = 0; @@ -1273,6 +1340,10 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + if (orco) { + GPU_vertbuf_bind_as_ssbo(src_extra_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(orco, binding_point++); + } BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1289,6 +1360,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPU_vertbuf_discard(patch_param_buffer); GPU_vertbuf_discard(patch_arrays_buffer); GPU_vertbuf_discard(src_buffer); + GPU_VERTBUF_DISCARD_SAFE(src_extra_buffer); } void draw_subdiv_extract_uvs(const DRWSubdivCache *cache, @@ -1552,12 +1624,11 @@ void draw_subdiv_build_tris_buffer(const DRWSubdivCache *cache, do_single_material ? SHADER_BUFFER_TRIS : SHADER_BUFFER_TRIS_MULTIPLE_MATERIALS, defines); GPU_shader_bind(shader); - if (!do_single_material) { - /* subdiv_polygon_offset is always at binding point 0 for each shader using it. */ - GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, 0); - } + int binding_point = 0; - int binding_point = 1; + /* subdiv_polygon_offset is always at binding point 0 for each shader using it. */ + GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); /* Outputs */ GPU_indexbuf_bind_as_ssbo(subdiv_tris, binding_point++); @@ -1611,7 +1682,9 @@ void draw_subdiv_build_fdots_buffers(const DRWSubdivCache *cache, get_patch_param_format()); evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface); - GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION_FACE_DOTS); + GPUShader *shader = get_patch_evaluation_shader( + fdots_nor ? SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS : + SHADER_PATCH_EVALUATION_FACE_DOTS); GPU_shader_bind(shader); int binding_point = 0; @@ -1624,7 +1697,11 @@ void draw_subdiv_build_fdots_buffers(const DRWSubdivCache *cache, GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(fdots_pos, binding_point++); - GPU_vertbuf_bind_as_ssbo(fdots_nor, binding_point++); + /* F-dots normals may not be requested, still reserve the binding point. */ + if (fdots_nor) { + GPU_vertbuf_bind_as_ssbo(fdots_nor, binding_point); + } + binding_point++; GPU_indexbuf_bind_as_ssbo(fdots_indices, binding_point++); GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); @@ -1646,11 +1723,13 @@ void draw_subdiv_build_fdots_buffers(const DRWSubdivCache *cache, void draw_subdiv_build_lines_buffer(const DRWSubdivCache *cache, GPUIndexBuf *lines_indices) { - GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_LINES, nullptr); + GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_LINES, "#define SUBDIV_POLYGON_OFFSET\n"); GPU_shader_bind(shader); int binding_point = 0; + GPU_vertbuf_bind_as_ssbo(cache->subdiv_polygon_offset_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(cache->edges_orig_index, binding_point++); + GPU_vertbuf_bind_as_ssbo(cache->extra_coarse_face_data, binding_point++); GPU_indexbuf_bind_as_ssbo(lines_indices, binding_point++); BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); @@ -1665,12 +1744,14 @@ void draw_subdiv_build_lines_buffer(const DRWSubdivCache *cache, GPUIndexBuf *li void draw_subdiv_build_lines_loose_buffer(const DRWSubdivCache *cache, GPUIndexBuf *lines_indices, + GPUVertBuf *lines_flags, uint num_loose_edges) { GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_LINES_LOOSE, "#define LINES_LOOSE\n"); GPU_shader_bind(shader); - GPU_indexbuf_bind_as_ssbo(lines_indices, 1); + GPU_indexbuf_bind_as_ssbo(lines_indices, 3); + GPU_vertbuf_bind_as_ssbo(lines_flags, 4); drw_subdiv_compute_dispatch(cache, shader, 0, 0, num_loose_edges); @@ -1905,7 +1986,7 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const bool do_final, const bool do_uvedit, const ToolSettings *ts, - const bool /*use_hide*/, + const bool use_hide, OpenSubdiv_EvaluatorCache *evaluator_cache) { SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>( @@ -1935,6 +2016,8 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, return false; } + draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval); + if (!BKE_subdiv_eval_begin_from_mesh( subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) { /* This could happen in two situations: @@ -1981,6 +2064,7 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, MeshRenderData *mr = mesh_render_data_create( ob, mesh, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); + mr->use_hide = use_hide; draw_subdiv_cache_update_extra_coarse_face_data(draw_cache, mesh_eval, mr); diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 84e79cd8be9..b6b0c94f4bf 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -13,6 +13,7 @@ extern "C" { #endif +struct CurvesUniformBufPool; struct DRWShadingGroup; struct FluidModifierData; struct GPUMaterial; @@ -82,7 +83,8 @@ struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object, struct DRWShadingGroup *shgrp, struct GPUMaterial *gpu_material); -void DRW_curves_init(void); +void DRW_curves_init(struct DRWData *drw_data); +void DRW_curves_ubos_pool_free(struct CurvesUniformBufPool *pool); void DRW_curves_update(void); void DRW_curves_free(void); diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 2edf596ac63..d90c63b680e 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -10,6 +10,7 @@ #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" #include "GPU_batch.h" @@ -20,9 +21,13 @@ #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +#include "DRW_gpu_wrapper.hh" #include "DRW_render.h" +#include "draw_cache_impl.h" +#include "draw_curves_private.h" #include "draw_hair_private.h" +#include "draw_manager.h" #include "draw_shader.h" #ifndef __APPLE__ @@ -61,16 +66,43 @@ static GPUVertBuf *g_dummy_vbo = nullptr; static GPUTexture *g_dummy_texture = nullptr; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ +using CurvesInfosBuf = blender::draw::UniformBuffer<CurvesInfos>; + +struct CurvesUniformBufPool { + blender::Vector<std::unique_ptr<CurvesInfosBuf>> ubos; + int used = 0; + + void reset() + { + used = 0; + } + + CurvesInfosBuf &alloc() + { + if (used >= ubos.size()) { + ubos.append(std::make_unique<CurvesInfosBuf>()); + return *ubos.last().get(); + } + return *ubos[used++].get(); + } +}; + static GPUShader *curves_eval_shader_get(CurvesEvalShader type) { return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get()); } -void DRW_curves_init(void) +void DRW_curves_init(DRWData *drw_data) { /* Initialize legacy hair too, to avoid verbosity in callers. */ DRW_hair_init(); + if (drw_data->curves_ubos == nullptr) { + drw_data->curves_ubos = MEM_new<CurvesUniformBufPool>("CurvesUniformBufPool"); + } + CurvesUniformBufPool *pool = drw_data->curves_ubos; + pool->reset(); + #if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0); #else @@ -94,63 +126,120 @@ void DRW_curves_init(void) } } +void DRW_curves_ubos_pool_free(CurvesUniformBufPool *pool) +{ + MEM_delete(pool); +} + static void drw_curves_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, CurvesEvalCache *cache, + GPUTexture *tex, const int subdiv) { - DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", tex); DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); } +static void drw_curves_cache_update_compute(CurvesEvalCache *cache, + const int subdiv, + const int strands_len, + GPUVertBuf *buffer, + GPUTexture *tex) +{ + GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); + drw_curves_cache_shgrp_attach_resources(shgrp, cache, tex, subdiv); + DRW_shgroup_vertex_buffer(shgrp, "posTime", buffer); + + const int max_strands_per_call = GPU_max_work_group_count(0); + int strands_start = 0; + while (strands_start < strands_len) { + int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); + DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); + DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); + strands_start += batch_strands_len; + } +} + static void drw_curves_cache_update_compute(CurvesEvalCache *cache, const int subdiv) { const int strands_len = cache->strands_len; const int final_points_len = cache->final[subdiv].strands_res * strands_len; - if (final_points_len > 0) { - GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); - DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); - drw_curves_cache_shgrp_attach_resources(shgrp, cache, subdiv); - DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf); - - const int max_strands_per_call = GPU_max_work_group_count(0); - int strands_start = 0; - while (strands_start < strands_len) { - int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); - DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); - DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); - DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); - strands_start += batch_strands_len; + if (final_points_len == 0) { + return; + } + + drw_curves_cache_update_compute( + cache, subdiv, strands_len, cache->final[subdiv].proc_buf, cache->point_tex); + + const DRW_Attributes &attrs = cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + /* Only refine point attributes. */ + if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) { + continue; } + + drw_curves_cache_update_compute(cache, + subdiv, + strands_len, + cache->final[subdiv].attributes_buf[i], + cache->proc_attributes_tex[i]); } } -static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv) +static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, + GPUVertBuf *vbo, + GPUTexture *tex, + const int subdiv, + const int final_points_len) { - const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); + GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); #ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass, vbo); #else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__); + pr_call->next = g_tf_calls; + pr_call->vbo = vbo; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); #endif - drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); + drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, tex, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); +} + +static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv) +{ + const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len == 0) { + return; + } + + drw_curves_cache_update_transform_feedback( + cache, cache->final[subdiv].proc_buf, cache->point_tex, subdiv, final_points_len); + + const DRW_Attributes &attrs = cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + /* Only refine point attributes. */ + if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) { + continue; + } + + drw_curves_cache_update_transform_feedback(cache, + cache->final[subdiv].attributes_buf[i], + cache->proc_attributes_tex[i], + subdiv, + final_points_len); } } @@ -186,12 +275,34 @@ GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) return cache->final[subdiv].proc_buf; } +static int attribute_index_in_material(GPUMaterial *gpu_material, const char *name) +{ + if (!gpu_material) { + return -1; + } + + int index = 0; + + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + if (STREQ(gpu_attr->name, name)) { + return index; + } + + index++; + } + + return -1; +} + DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRWShadingGroup *shgrp_parent, GPUMaterial *gpu_material) { const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; + CurvesUniformBufPool *pool = DST.vmempool->curves_ubos; + CurvesInfosBuf &curves_infos = pool->alloc(); int subdiv = scene->r.hair_subdiv; int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; @@ -218,6 +329,43 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, if (curves_cache->length_tex) { DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); } + + const DRW_Attributes &attrs = curves_cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + const DRW_AttributeRequest &request = attrs.requests[i]; + + char sampler_name[32]; + drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name); + + if (request.domain == ATTR_DOMAIN_CURVE) { + if (!curves_cache->proc_attributes_tex[i]) { + continue; + } + + DRW_shgroup_uniform_texture(shgrp, sampler_name, curves_cache->proc_attributes_tex[i]); + } + else { + if (!curves_cache->final[subdiv].attributes_tex[i]) { + continue; + } + DRW_shgroup_uniform_texture( + shgrp, sampler_name, curves_cache->final[subdiv].attributes_tex[i]); + } + + /* Some attributes may not be used in the shader anymore and were not garbage collected yet, so + * we need to find the right index for this attribute as uniforms defining the scope of the + * attributes are based on attribute loading order, which is itself based on the material's + * attributes. */ + const int index = attribute_index_in_material(gpu_material, request.attribute_name); + if (index != -1) { + curves_infos.is_point_attribute[index] = request.domain == ATTR_DOMAIN_POINT; + } + } + + curves_infos.push_update(); + + DRW_shgroup_uniform_block(shgrp, "drw_curves", curves_infos); + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); diff --git a/source/blender/draw/intern/draw_curves_private.h b/source/blender/draw/intern/draw_curves_private.h index 76d5f15319d..ed4dd50dfbe 100644 --- a/source/blender/draw/intern/draw_curves_private.h +++ b/source/blender/draw/intern/draw_curves_private.h @@ -7,6 +7,11 @@ #pragma once +#include "BKE_attribute.h" +#include "GPU_shader.h" + +#include "draw_attributes.h" + #ifdef __cplusplus extern "C" { #endif @@ -35,6 +40,23 @@ typedef struct CurvesEvalFinalCache { /* Points per curve, at least 2. */ int strands_res; + + /* Attributes currently being or about to be drawn. */ + DRW_Attributes attr_used; + + /* Attributes which were used at some point. This is used for garbage collection, to remove + * attributes which are not used in shaders anymore due to user edits. */ + DRW_Attributes attr_used_over_time; + + /* Last time, in seconds, the `attr_used` and `attr_used_over_time` were exactly the same. + * If the delta between this time and the current scene time is greater than the timeout set in + * user preferences (`U.vbotimeout`) then garbage collection is performed. */ + int last_attr_matching_time; + + /* Output of the subdivision stage: vertex buffers sized to subdiv level. This is only attributes + * on point domain. */ + GPUVertBuf *attributes_buf[GPU_MAX_ATTR]; + GPUTexture *attributes_tex[GPU_MAX_ATTR]; } CurvesEvalFinalCache; /* Curves procedural display: Evaluation is done on the GPU. */ @@ -56,6 +78,11 @@ typedef struct CurvesEvalCache { CurvesEvalFinalCache final[MAX_HAIR_SUBDIV]; + /* For point attributes, which need subdivision, these are the input data. + * For spline attributes, which need not subdivision, these are the final data. */ + GPUVertBuf *proc_attributes_buf[GPU_MAX_ATTR]; + GPUTexture *proc_attributes_tex[GPU_MAX_ATTR]; + int strands_len; int elems_len; int point_len; @@ -70,6 +97,8 @@ bool curves_ensure_procedural_data(struct Object *object, int subdiv, int thickness_res); +void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_fluid.c b/source/blender/draw/intern/draw_fluid.c index d3d4bbf505e..c34025ebe52 100644 --- a/source/blender/draw/intern/draw_fluid.c +++ b/source/blender/draw/intern/draw_fluid.c @@ -20,6 +20,8 @@ #include "BKE_colorband.h" +#include "IMB_colormanagement.h" + #include "GPU_texture.h" #include "draw_manager.h" @@ -52,7 +54,7 @@ static void create_flame_spectrum_texture(float *data) float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); - blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); + IMB_colormanagement_blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b3c4e21639d..1372b843442 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -479,6 +479,7 @@ void DRW_viewport_data_free(DRWData *drw_data) MEM_freeN(drw_data->obinfos_ubo); } DRW_volume_ubos_pool_free(drw_data->volume_grids_ubos); + DRW_curves_ubos_pool_free(drw_data->curves_ubos); MEM_freeN(drw_data); } @@ -1650,7 +1651,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_globals_update(); drw_debug_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2022,7 +2023,7 @@ void DRW_render_object_iter( void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph)) { const DRWContextState *draw_ctx = DRW_context_state_get(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2079,7 +2080,7 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, drw_manager_init(&DST, NULL, NULL); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2114,7 +2115,7 @@ void DRW_cache_restart(void) DST.buffer_finish_called = false; - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); } @@ -2433,7 +2434,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2607,7 +2608,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index aa0c472be04..6d384c599d8 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -538,6 +538,8 @@ typedef struct DRWData { struct DRWTexturePool *texture_pool; /** Per stereo view data. Contains engine data and default framebuffers. */ struct DRWViewData *view_data[2]; + /** Per draw-call curves object data. */ + struct CurvesUniformBufPool *curves_ubos; } DRWData; /* ------------- DRAW MANAGER ------------ */ diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index db128fffde7..94c0c53dab7 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GPU_SHADER +# include "GPU_shader.h" # include "GPU_shader_shared_utils.h" typedef struct ViewInfos ViewInfos; typedef struct ObjectMatrices ObjectMatrices; typedef struct ObjectInfos ObjectInfos; typedef struct VolumeInfos VolumeInfos; +typedef struct CurvesInfos CurvesInfos; #endif #define DRW_SHADER_SHARED_H @@ -16,6 +18,10 @@ typedef struct VolumeInfos VolumeInfos; /* Define the maximum number of grid we allow in a volume UBO. */ #define DRW_GRID_PER_VOLUME_MAX 16 +/* Define the maximum number of attribute we allow in a curves UBO. + * This should be kept in sync with `GPU_ATTR_MAX` */ +#define DRW_ATTRIBUTE_PER_CURVES_MAX 15 + struct ViewInfos { /* View matrices */ float4x4 persmat; @@ -79,6 +85,14 @@ struct VolumeInfos { }; BLI_STATIC_ASSERT_ALIGN(VolumeInfos, 16) +struct CurvesInfos { + /* Per attribute scope, follows loading order. + * NOTE: uint as bool in GLSL is 4 bytes. */ + uint is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX]; + int _pad; +}; +BLI_STATIC_ASSERT_ALIGN(CurvesInfos, 16) + #define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) #define ObjectInfo (drw_infos[resource_id].drw_Infos) #define ObjectColor (drw_infos[resource_id].drw_ObjectColor) diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 2d24d07e037..15c770a51c4 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -235,7 +235,9 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache, GPUVertBuf *src_custom_normals, GPUVertBuf *pos_nor); -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor); +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, + struct GPUVertBuf *pos_nor, + struct GPUVertBuf *orco); void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, struct GPUVertBuf *src_data, @@ -263,6 +265,7 @@ void draw_subdiv_build_lines_buffer(const DRWSubdivCache *cache, void draw_subdiv_build_lines_loose_buffer(const DRWSubdivCache *cache, struct GPUIndexBuf *lines_indices, + GPUVertBuf *lines_flags, uint num_loose_edges); void draw_subdiv_build_fdots_buffers(const DRWSubdivCache *cache, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index ce3ca428469..3cecaf81b8a 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -160,7 +160,7 @@ static void extract_lines_init_subdiv(const DRWSubdivCache *subdiv_cache, } static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, void *buffer, void *UNUSED(data)) { @@ -169,8 +169,31 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, return; } + /* Update flags for loose edges, points are already handled. */ + static GPUVertFormat format; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + + GPUVertBuf *flags = GPU_vertbuf_calloc(); + GPU_vertbuf_init_with_format(flags, &format); + + Span<DRWSubdivLooseEdge> loose_edges = draw_subdiv_cache_get_loose_edges(subdiv_cache); + GPU_vertbuf_data_alloc(flags, loose_edges.size()); + + uint *flags_data = static_cast<uint *>(GPU_vertbuf_get_data(flags)); + + const MEdge *medge = mr->medge; + + for (DRWSubdivLooseEdge edge : loose_edges) { + *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0; + } + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buffer); - draw_subdiv_build_lines_loose_buffer(subdiv_cache, ibo, static_cast<uint>(loose_geom.edge_len)); + draw_subdiv_build_lines_loose_buffer( + subdiv_cache, ibo, flags, static_cast<uint>(loose_geom.edge_len)); + + GPU_vertbuf_discard(flags); } constexpr MeshExtract create_extractor_lines() diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index 272963f3fd5..503ce0e79e9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -156,7 +156,8 @@ static void extract_points_init_subdiv(const DRWSubdivCache *subdiv_cache, static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb, const MeshRenderData *mr, const DRWSubdivCache *subdiv_cache, - uint subdiv_quad_index) + uint subdiv_quad_index, + bool for_bmesh) { int *subdiv_loop_vert_index = (int *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index); uint start_loop_idx = subdiv_quad_index * 4; @@ -172,6 +173,21 @@ static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb, continue; } + if (for_bmesh) { + const BMVert *mv = BM_vert_at_index(mr->bm, coarse_vertex_index); + if (BM_elem_flag_test(mv, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_restart(elb, coarse_vertex_index); + continue; + } + } + else { + const MVert *mv = &mr->mvert[coarse_vertex_index]; + if (mr->use_hide && (mv->flag & ME_HIDE)) { + GPU_indexbuf_set_point_restart(elb, coarse_vertex_index); + continue; + } + } + GPU_indexbuf_set_point_vert(elb, coarse_vertex_index, i); } } @@ -183,7 +199,7 @@ static void extract_points_iter_subdiv_bm(const DRWSubdivCache *subdiv_cache, const BMFace *UNUSED(coarse_quad)) { GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); - extract_points_iter_subdiv_common(elb, mr, subdiv_cache, subdiv_quad_index); + extract_points_iter_subdiv_common(elb, mr, subdiv_cache, subdiv_quad_index, true); } static void extract_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_cache, @@ -193,7 +209,7 @@ static void extract_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_cache, const MPoly *UNUSED(coarse_quad)) { GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); - extract_points_iter_subdiv_common(elb, mr, subdiv_cache, subdiv_quad_index); + extract_points_iter_subdiv_common(elb, mr, subdiv_cache, subdiv_quad_index, false); } static void extract_points_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index d275b672366..d595abc6dd3 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -14,6 +14,7 @@ #include "BKE_attribute.h" +#include "draw_attributes.h" #include "draw_subdivision.h" #include "extract_mesh.h" @@ -284,7 +285,7 @@ static void extract_attr_init(const MeshRenderData *mr, void *UNUSED(tls_data), int index) { - const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_Attributes *attrs_used = &cache->attr_used; const DRW_AttributeRequest &request = attrs_used->requests[index]; GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); @@ -337,7 +338,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, void *UNUSED(tls_data), int index) { - const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_Attributes *attrs_used = &cache->attr_used; const DRW_AttributeRequest &request = attrs_used->requests[index]; Mesh *coarse_mesh = subdiv_cache->mesh; @@ -346,7 +347,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, /* Prepare VBO for coarse data. The compute shader only expects floats. */ GPUVertBuf *src_data = GPU_vertbuf_calloc(); - static GPUVertFormat coarse_format = {0}; + GPUVertFormat coarse_format = {0}; GPU_vertformat_attr_add(&coarse_format, "data", GPU_COMP_F32, dimensions, GPU_FETCH_FLOAT); GPU_vertbuf_init_with_format_ex(src_data, &coarse_format, GPU_USAGE_STATIC); GPU_vertbuf_data_alloc(src_data, static_cast<uint32_t>(coarse_mesh->totloop)); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc index 14f61dcd739..fb4b95885fc 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc @@ -236,7 +236,7 @@ static void extract_edituv_stretch_angle_init_subdiv(const DRWSubdivCache *subdi draw_subdiv_get_pos_nor_format(), subdiv_cache->num_subdiv_loops + loose_geom.loop_len); - draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor); + draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor, nullptr); } /* UVs are stored contiguously so we need to compute the offset in the UVs buffer for the active diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc index c2b4d389b7c..1671a1cd1e7 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc @@ -110,8 +110,11 @@ static void extract_fdots_init_subdiv(const DRWSubdivCache *subdiv_cache, GPUVertBuf *fdots_nor_vbo = cache->final.buff.vbo.fdots_nor; GPUIndexBuf *fdots_pos_ibo = cache->final.buff.ibo.fdots; - GPU_vertbuf_init_build_on_device( - fdots_nor_vbo, get_fdots_nor_format_subdiv(), subdiv_cache->num_coarse_poly); + /* The normals may not be requested. */ + if (fdots_nor_vbo) { + GPU_vertbuf_init_build_on_device( + fdots_nor_vbo, get_fdots_nor_format_subdiv(), subdiv_cache->num_coarse_poly); + } GPU_vertbuf_init_build_on_device( fdots_pos_vbo, get_fdots_pos_format(), subdiv_cache->num_coarse_poly); GPU_indexbuf_init_build_on_device(fdots_pos_ibo, subdiv_cache->num_coarse_poly); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index b45a73a27c0..94674a54f12 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -7,8 +7,6 @@ #include "extract_mesh.h" -#include "draw_subdivision.h" - namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -79,77 +77,12 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buffer, - void *UNUSED(data)) -{ - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex - * attributes. This is a substantial waste of video-ram and should be done another way. - * Unfortunately, at the time of writing, I did not found any other "non disruptive" - * alternative. */ - GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); - GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); - - GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc(); - /* Dynamic as we upload and interpolate layers one at a time. */ - GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC); - GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len); - - float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo)); - - CustomData *cd_vdata = &mr->me->vdata; - const float(*orco)[3] = static_cast<const float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); - - if (mr->extract_type == MR_EXTRACT_MESH) { - const MLoop *mloop = mr->mloop; - const MPoly *mp = mr->mpoly; - - int ml_index = 0; - for (int i = 0; i < mr->poly_len; i++, mp++) { - const MLoop *ml = &mloop[mp->loopstart]; - - for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) { - float *loop_orco = coarse_vbo_data[ml_index]; - copy_v3_v3(loop_orco, orco[ml->v]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - } - } - else { - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter; - BMLoop *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - float *loop_orco = coarse_vbo_data[l_index]; - copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } while ((l_iter = l_iter->next) != l_first); - } - } - - draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false); - - GPU_vertbuf_discard(coarse_vbo); -} - constexpr MeshExtract create_extractor_orco() { MeshExtract extractor = {nullptr}; extractor.init = extract_orco_init; extractor.iter_poly_bm = extract_orco_iter_poly_bm; extractor.iter_poly_mesh = extract_orco_iter_poly_mesh; - extractor.init_subdiv = extract_orco_init_subdiv; extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_Orco_Data); extractor.use_threading = true; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index c8c91d45542..f80b33e28f2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -201,7 +201,7 @@ static GPUVertFormat *get_custom_normals_format() static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), + struct MeshBatchCache *cache, void *buffer, void *UNUSED(data)) { @@ -216,7 +216,21 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, return; } - draw_subdiv_extract_pos_nor(subdiv_cache, vbo); + GPUVertBuf *orco_vbo = cache->final.buff.vbo.orco; + + if (orco_vbo) { + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attributes. This is a substantial waste of video-ram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + GPU_vertbuf_init_build_on_device(orco_vbo, &format, subdiv_cache->num_subdiv_loops); + } + + draw_subdiv_extract_pos_nor(subdiv_cache, vbo, orco_vbo); if (subdiv_cache->use_custom_loop_normals) { Mesh *coarse_mesh = subdiv_cache->mesh; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index e5dd025787d..fa5bf35198b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -271,8 +271,6 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, blender::Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers); - gpuMeshVcol *vcol = mesh_vcol; - /* Index of the vertex color layer in the compact buffer. Used vertex color layers are stored in * a single buffer. */ int pack_layer_index = 0; @@ -287,10 +285,10 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, if (layer_i == -1) { printf("%s: missing color layer %s\n", __func__, ref.layer->name); - vcol += coarse_mesh->totloop; continue; } + gpuMeshVcol *vcol = mesh_vcol; MLoopCol *mcol = nullptr; MPropCol *pcol = nullptr; diff --git a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl index e58cfaae40d..123c493b572 100644 --- a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl +++ b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl @@ -393,7 +393,7 @@ vec4 gpencil_vertex(ivec4 ma, col2, fcol1, viewport_size, - 0, + 0u, vec2(1.0, 0.0), out_P, out_N, diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 1ac26c91b93..51f3c890df8 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -110,6 +110,7 @@ float len_squared(vec3 a) { return dot(a, a); } float len_squared(vec2 a) { return dot(a, a); } bool flag_test(uint flag, uint val) { return (flag & val) != 0u; } +bool flag_test(int flag, uint val) { return flag_test(uint(flag), val); } bool flag_test(int flag, int val) { return (flag & val) != 0; } void set_flag_from_test(inout uint value, bool test, uint flag) { if (test) { value |= flag; } else { value &= ~flag; } } diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl index 3cbb9f980f3..3244b7960d8 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl @@ -1,22 +1,42 @@ /* To be compiled with common_subdiv_lib.glsl */ -layout(std430, binding = 0) readonly buffer inputEdgeOrigIndex +layout(std430, binding = 1) readonly buffer inputEdgeOrigIndex { int input_origindex[]; }; -layout(std430, binding = 1) writeonly buffer outputLinesIndices +layout(std430, binding = 2) readonly restrict buffer extraCoarseFaceData +{ + uint extra_coarse_face_data[]; +}; + +layout(std430, binding = 3) writeonly buffer outputLinesIndices { uint output_lines[]; }; +layout(std430, binding = 4) readonly buffer LinesLooseFlags +{ + uint lines_loose_flags[]; +}; + #ifndef LINES_LOOSE -void emit_line(uint line_offset, uint start_loop_index, uint corner_index) + +bool is_face_hidden(uint coarse_quad_index) +{ + return (extra_coarse_face_data[coarse_quad_index] & coarse_face_hidden_mask) != 0; +} + +void emit_line(uint line_offset, uint quad_index, uint start_loop_index, uint corner_index) { uint vertex_index = start_loop_index + corner_index; - if (input_origindex[vertex_index] == ORIGINDEX_NONE && optimal_display) { + uint coarse_quad_index = coarse_polygon_index_from_subdiv_quad_index(quad_index, + coarse_poly_count); + + if (is_face_hidden(coarse_quad_index) || + (input_origindex[vertex_index] == ORIGINDEX_NONE && optimal_display)) { output_lines[line_offset + 0] = 0xffffffff; output_lines[line_offset + 1] = 0xffffffff; } @@ -41,8 +61,17 @@ void main() /* In the loose lines case, we execute for each line, with two vertices per line. */ uint line_offset = edge_loose_offset + index * 2; uint loop_index = num_subdiv_loops + index * 2; - output_lines[line_offset] = loop_index; - output_lines[line_offset + 1] = loop_index + 1; + + if (lines_loose_flags[index] != 0) { + /* Line is hidden. */ + output_lines[line_offset] = 0xffffffff; + output_lines[line_offset + 1] = 0xffffffff; + } + else { + output_lines[line_offset] = loop_index; + output_lines[line_offset + 1] = loop_index + 1; + } + #else /* We execute for each quad, so the start index of the loop is quad_index * 4. */ uint start_loop_index = index * 4; @@ -51,7 +80,7 @@ void main() uint start_line_index = index * 8; for (int i = 0; i < 4; i++) { - emit_line(start_line_index + i * 2, start_loop_index, i); + emit_line(start_line_index + i * 2, index, start_loop_index, i); } #endif } diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl index 3dccc82541e..ce3c8478d3f 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl @@ -3,18 +3,28 @@ /* Generate triangles from subdivision quads indices. */ -layout(std430, binding = 1) writeonly buffer outputTriangles +layout(std430, binding = 1) readonly restrict buffer extraCoarseFaceData +{ + uint extra_coarse_face_data[]; +}; + +layout(std430, binding = 2) writeonly buffer outputTriangles { uint output_tris[]; }; #ifndef SINGLE_MATERIAL -layout(std430, binding = 2) readonly buffer inputPolygonMatOffset +layout(std430, binding = 3) readonly buffer inputPolygonMatOffset { int polygon_mat_offset[]; }; #endif +bool is_face_hidden(uint coarse_quad_index) +{ + return (extra_coarse_face_data[coarse_quad_index] & coarse_face_hidden_mask) != 0; +} + void main() { uint quad_index = get_global_invocation_index(); @@ -24,20 +34,31 @@ void main() uint loop_index = quad_index * 4; + uint coarse_quad_index = coarse_polygon_index_from_subdiv_quad_index(quad_index, + coarse_poly_count); + #ifdef SINGLE_MATERIAL uint triangle_loop_index = quad_index * 6; #else - uint coarse_quad_index = coarse_polygon_index_from_subdiv_quad_index(quad_index, - coarse_poly_count); int mat_offset = polygon_mat_offset[coarse_quad_index]; int triangle_loop_index = (int(quad_index) + mat_offset) * 6; #endif - output_tris[triangle_loop_index + 0] = loop_index + 0; - output_tris[triangle_loop_index + 1] = loop_index + 1; - output_tris[triangle_loop_index + 2] = loop_index + 2; - output_tris[triangle_loop_index + 3] = loop_index + 0; - output_tris[triangle_loop_index + 4] = loop_index + 2; - output_tris[triangle_loop_index + 5] = loop_index + 3; + if (is_face_hidden(coarse_quad_index)) { + output_tris[triangle_loop_index + 0] = 0xffffffff; + output_tris[triangle_loop_index + 1] = 0xffffffff; + output_tris[triangle_loop_index + 2] = 0xffffffff; + output_tris[triangle_loop_index + 3] = 0xffffffff; + output_tris[triangle_loop_index + 4] = 0xffffffff; + output_tris[triangle_loop_index + 5] = 0xffffffff; + } + else { + output_tris[triangle_loop_index + 0] = loop_index + 0; + output_tris[triangle_loop_index + 1] = loop_index + 1; + output_tris[triangle_loop_index + 2] = loop_index + 2; + output_tris[triangle_loop_index + 3] = loop_index + 0; + output_tris[triangle_loop_index + 4] = loop_index + 2; + output_tris[triangle_loop_index + 5] = loop_index + 3; + } } diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl index ce324249446..55e4ac20271 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl @@ -31,6 +31,7 @@ layout(std140) uniform shader_data uint coarse_face_select_mask; uint coarse_face_smooth_mask; uint coarse_face_active_mask; + uint coarse_face_hidden_mask; uint coarse_face_loopstart_mask; /* Total number of elements to process. */ diff --git a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl index 65cf4ebb90f..0ffb216fc6f 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl @@ -70,10 +70,12 @@ layout(std430, binding = 8) writeonly buffer outputVertices FDotVert output_verts[]; }; +# ifdef FDOTS_NORMALS layout(std430, binding = 9) writeonly buffer outputNormals { FDotNor output_nors[]; }; +# endif layout(std430, binding = 10) writeonly buffer outputFdotsIndices { @@ -89,6 +91,16 @@ layout(std430, binding = 8) writeonly buffer outputVertexData { PosNorLoop output_verts[]; }; +# if defined(ORCO_EVALUATION) +layout(std430, binding = 9) buffer src_extra_buffer +{ + float srcExtraVertexBuffer[]; +}; +layout(std430, binding = 10) writeonly buffer outputOrcoData +{ + vec4 output_orcos[]; +}; +# endif #endif vec2 read_vec2(int index) @@ -108,6 +120,17 @@ vec3 read_vec3(int index) return result; } +#if defined(ORCO_EVALUATION) +vec3 read_vec3_extra(int index) +{ + vec3 result; + result.x = srcExtraVertexBuffer[index * 3]; + result.y = srcExtraVertexBuffer[index * 3 + 1]; + result.z = srcExtraVertexBuffer[index * 3 + 2]; + return result; +} +#endif + OsdPatchArray GetPatchArray(int arrayIndex) { return patchArrayBuffer[arrayIndex]; @@ -290,6 +313,31 @@ void evaluate_patches_limits( dv += src_vertex * wDv[cv]; } } + +# if defined(ORCO_EVALUATION) +/* Evaluate the patches limits from the extra source vertex buffer. */ +void evaluate_patches_limits_extra(int patch_index, float u, float v, inout vec3 dst) +{ + OsdPatchCoord coord = GetPatchCoord(patch_index, u, v); + OsdPatchArray array = GetPatchArray(coord.arrayIndex); + OsdPatchParam param = GetPatchParam(coord.patchIndex); + + int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc; + + float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20]; + int nPoints = OsdEvaluatePatchBasis( + patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv); + + int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase); + + for (int cv = 0; cv < nPoints; ++cv) { + int index = patchIndexBuffer[indexBase + cv]; + vec3 src_vertex = read_vec3_extra(index); + + dst += src_vertex * wP[cv]; + } +} +# endif #endif /* ------------------------------------------------------------------------------ @@ -341,6 +389,11 @@ float get_face_flag(uint coarse_quad_index) return 0.0; } +bool is_face_hidden(uint coarse_quad_index) +{ + return (extra_coarse_face_data[coarse_quad_index] & coarse_face_hidden_mask) != 0; +} + void main() { /* We execute for each coarse quad. */ @@ -370,8 +423,16 @@ void main() fnor.flag = get_face_flag(coarse_quad_index); output_verts[coarse_quad_index] = vert; +# ifdef FDOTS_NORMALS output_nors[coarse_quad_index] = fnor; - output_indices[coarse_quad_index] = coarse_quad_index; +# endif + + if (is_face_hidden(coarse_quad_index)) { + output_indices[coarse_quad_index] = 0xffffffff; + } + else { + output_indices[coarse_quad_index] = coarse_quad_index; + } } #else void main() @@ -407,6 +468,16 @@ void main() set_vertex_pos(vertex_data, pos); set_vertex_nor(vertex_data, nor, flag); output_verts[loop_index] = vertex_data; + +# if defined(ORCO_EVALUATION) + pos = vec3(0.0); + evaluate_patches_limits_extra(patch_co.patch_index, uv.x, uv.y, pos); + + /* Set w = 0.0 to indicate that this is not a generic attribute. + * See comments in `extract_mesh_vbo_orco.cc`. */ + vec4 orco_data = vec4(pos, 0.0); + output_orcos[loop_index] = orco_data; +# endif } } #endif diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh index c74a043ec97..8fd55ea351f 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -10,3 +10,7 @@ GPU_SHADER_CREATE_INFO(draw_object_infos) GPU_SHADER_CREATE_INFO(draw_volume_infos) .typedef_source("draw_shader_shared.h") .uniform_buf(2, "VolumeInfos", "drw_volume", Frequency::BATCH); + +GPU_SHADER_CREATE_INFO(draw_curves_infos) + .typedef_source("draw_shader_shared.h") + .uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH); diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 1a3ab100768..8519b2061f2 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -543,6 +543,9 @@ void ED_markers_draw(const bContext *C, int flag) View2D *v2d = UI_view2d_fromcontext(C); int cfra = CTX_data_scene(C)->r.cfra; + const float line_width = GPU_line_width_get(); + GPU_line_width(1.0f); + rctf markers_region_rect; get_marker_region_rect(v2d, &markers_region_rect); @@ -575,6 +578,7 @@ void ED_markers_draw(const bContext *C, int flag) } } + GPU_line_width(line_width); GPU_matrix_pop(); } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 14a3b958ea6..941125b9ad5 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2051,6 +2051,8 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + RNA_def_property_string_search_func_runtime( + prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } @@ -2246,6 +2248,8 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + RNA_def_property_string_search_func_runtime( + prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 6fcdd21bad8..97b81277008 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -708,6 +708,72 @@ KeyingSet *ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *tra return ANIM_builtin_keyingset_get_named(NULL, transformKSName); } +static void anim_keyingset_visit_for_search_impl(const bContext *C, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data, + const bool use_poll) +{ + /* Poll requires context. */ + if (use_poll && (C == NULL)) { + return; + } + + Scene *scene = C ? CTX_data_scene(C) : NULL; + KeyingSet *ks; + + /* Active Keying Set. */ + if (!use_poll || (scene && scene->active_keyingset)) { + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = "__ACTIVE__"; + visit_params.info = "Active Keying Set"; + visit_fn(visit_user_data, &visit_params); + } + + /* User-defined Keying Sets. */ + if (scene && scene->keyingsets.first) { + for (ks = scene->keyingsets.first; ks; ks = ks->next) { + if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) { + continue; + } + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = ks->idname; + visit_params.info = ks->name; + visit_fn(visit_user_data, &visit_params); + } + } + + /* Builtin Keying Sets. */ + for (ks = builtin_keyingsets.first; ks; ks = ks->next) { + if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) { + continue; + } + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = ks->idname; + visit_params.info = ks->name; + visit_fn(visit_user_data, &visit_params); + } +} + +void ANIM_keyingset_visit_for_search(const bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, false); +} + +void ANIM_keyingset_visit_for_search_no_poll(const bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, true); +} + /* Menu of All Keying Sets ----------------------------- */ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C, diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 9bedfb6ee58..59370b53995 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -777,6 +777,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_cut ops.curves.sculpt_delete ops.curves.sculpt_grow_shrink + ops.curves.sculpt_pinch ops.curves.sculpt_puff ops.curves.sculpt_snake_hook ops.generic.cursor diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 6a730225da9..8c0147612fb 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -351,6 +351,19 @@ int ANIM_scene_get_keyingset_index(struct Scene *scene, struct KeyingSet *ks); struct KeyingSet *ANIM_get_keyingset_for_autokeying(const struct Scene *scene, const char *transformKSName); +void ANIM_keyingset_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + +void ANIM_keyingset_visit_for_search_no_poll(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); /** * Dynamically populate an enum of Keying Sets. */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index d69b2a47552..30a98129ee6 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -536,12 +536,12 @@ void ED_mesh_geometry_clear(struct Mesh *mesh); void ED_mesh_update(struct Mesh *mesh, struct bContext *C, bool calc_edges, bool calc_edges_loose); -void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name); -int ED_mesh_uv_texture_add( +void ED_mesh_uv_ensure(struct Mesh *me, const char *name); +int ED_mesh_uv_add( struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); -bool ED_mesh_uv_texture_remove_index(struct Mesh *me, int n); -bool ED_mesh_uv_texture_remove_active(struct Mesh *me); -bool ED_mesh_uv_texture_remove_named(struct Mesh *me, const char *name); +bool ED_mesh_uv_remove_index(struct Mesh *me, int n); +bool ED_mesh_uv_remove_active(struct Mesh *me); +bool ED_mesh_uv_remove_named(struct Mesh *me, const char *name); void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me); /** * Without a #bContext, called when UV-editing. diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 28452ba8db9..0078c1087a0 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -234,7 +234,7 @@ struct Base *ED_object_add_duplicate(struct Main *bmain, void ED_object_parent(struct Object *ob, struct Object *parent, int type, const char *substr); char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, - struct PointerRNA *properties, + const char *name, const int mval[2]); /* bitflags for enter/exit editmode */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b35911791b7..8503779f629 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -207,7 +207,7 @@ enum { UI_BUT_INACTIVE = 1 << 18, UI_BUT_LAST_ACTIVE = 1 << 19, UI_BUT_UNDO = 1 << 20, - UI_BUT_IMMEDIATE = 1 << 21, + /* UNUSED = 1 << 21, */ UI_BUT_NO_UTF8 = 1 << 22, /** For popups, pressing return activates this button, overriding the highlighted button. @@ -461,7 +461,6 @@ void UI_draw_safe_areas(uint pos, enum { UI_SCROLL_PRESSED = 1 << 0, UI_SCROLL_ARROWS = 1 << 1, - UI_SCROLL_NO_OUTLINE = 1 << 2, }; /** * Function in use for buttons and for view2d sliders. @@ -1656,15 +1655,16 @@ void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_f * \param name: Text to display for the item. * \param poin: Opaque pointer (for use by the caller). * \param iconid: The icon, #ICON_NONE for no icon. - * \param state: The buttons state flag, compatible with #uiBut.flag, - * typically #UI_BUT_DISABLED / #UI_BUT_INACTIVE. + * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically + * #UI_BUT_DISABLED, #UI_BUT_INACTIVE or #UI_BUT_HAS_SEP_CHAR. + * * \return false if there is nothing to add. */ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + int but_flag, uint8_t name_prefix_offset); /** diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 926d1664b5e..d1a92da3853 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -542,7 +542,7 @@ static bool but_copypaste_profile_alive = false; bool ui_but_is_editing(const uiBut *but) { - uiHandleButtonData *data = but->active; + const uiHandleButtonData *data = but->active; return (data && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING)); } @@ -660,21 +660,23 @@ static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx) static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop) { /* Not very elegant, but ensures preference changes force re-save. */ - bool tag = false; - if (prop && !(RNA_property_flag(prop) & PROP_NO_DEG_UPDATE)) { - StructRNA *base = RNA_struct_base(ptr->type); - if (base == NULL) { - base = ptr->type; - } - if (ELEM(base, - &RNA_AddonPreferences, - &RNA_KeyConfigPreferences, - &RNA_KeyMapItem, - &RNA_UserAssetLibrary)) { - tag = true; - } + + if (!prop) { + return false; } - return tag; + if (RNA_property_flag(prop) & PROP_NO_DEG_UPDATE) { + return false; + } + + StructRNA *base = RNA_struct_base(ptr->type); + if (base == NULL) { + base = ptr->type; + } + return ELEM(base, + &RNA_AddonPreferences, + &RNA_KeyConfigPreferences, + &RNA_KeyMapItem, + &RNA_UserAssetLibrary); } bool UI_but_is_userdef(const uiBut *but) @@ -900,64 +902,66 @@ static void ui_apply_but_func(bContext *C, uiBut *but) /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ static void ui_apply_but_undo(uiBut *but) { - if (but->flag & UI_BUT_UNDO) { - const char *str = NULL; - size_t str_len_clip = SIZE_MAX - 1; - bool skip_undo = false; + if (!(but->flag & UI_BUT_UNDO)) { + return; + } - /* define which string to use for undo */ - if (but->type == UI_BTYPE_MENU) { - str = but->drawstr; - str_len_clip = ui_but_drawstr_len_without_sep_char(but); - } - else if (but->drawstr[0]) { - str = but->drawstr; - str_len_clip = ui_but_drawstr_len_without_sep_char(but); - } - else { - str = but->tip; - str_len_clip = ui_but_tip_len_only_first_line(but); - } + const char *str = NULL; + size_t str_len_clip = SIZE_MAX - 1; + bool skip_undo = false; - /* fallback, else we don't get an undo! */ - if (str == NULL || str[0] == '\0' || str_len_clip == 0) { - str = "Unknown Action"; - str_len_clip = strlen(str); - } + /* define which string to use for undo */ + if (but->type == UI_BTYPE_MENU) { + str = but->drawstr; + str_len_clip = ui_but_drawstr_len_without_sep_char(but); + } + else if (but->drawstr[0]) { + str = but->drawstr; + str_len_clip = ui_but_drawstr_len_without_sep_char(but); + } + else { + str = but->tip; + str_len_clip = ui_but_tip_len_only_first_line(but); + } - /* Optionally override undo when undo system doesn't support storing properties. */ - if (but->rnapoin.owner_id) { - /* Exception for renaming ID data, we always need undo pushes in this case, - * because undo systems track data by their ID, see: T67002. */ - /* Exception for active shape-key, since changing this in edit-mode updates - * the shape key from object mode data. */ - if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { - /* pass */ - } - else { - ID *id = but->rnapoin.owner_id; - if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { - skip_undo = true; - } - } - } + /* fallback, else we don't get an undo! */ + if (str == NULL || str[0] == '\0' || str_len_clip == 0) { + str = "Unknown Action"; + str_len_clip = strlen(str); + } - if (skip_undo == false) { - /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo - * steps to be written which cause lag: T71434. */ - if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) { + /* Optionally override undo when undo system doesn't support storing properties. */ + if (but->rnapoin.owner_id) { + /* Exception for renaming ID data, we always need undo pushes in this case, + * because undo systems track data by their ID, see: T67002. */ + /* Exception for active shape-key, since changing this in edit-mode updates + * the shape key from object mode data. */ + if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { + /* pass */ + } + else { + ID *id = but->rnapoin.owner_id; + if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { skip_undo = true; } } + } - if (skip_undo) { - str = ""; + if (skip_undo == false) { + /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo + * steps to be written which cause lag: T71434. */ + if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) { + skip_undo = true; } + } - /* delayed, after all other funcs run, popups are closed, etc */ - uiAfterFunc *after = ui_afterfunc_new(); - BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr))); + if (skip_undo) { + str = ""; } + + /* delayed, after all other funcs run, popups are closed, etc */ + uiAfterFunc *after = ui_afterfunc_new(); + BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr))); } static void ui_apply_but_autokey(bContext *C, uiBut *but) @@ -967,21 +971,21 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but) /* try autokey */ ui_but_anim_autokey(C, but, scene, scene->r.cfra); - /* make a little report about what we've done! */ - if (but->rnaprop) { - char *buf; + if (!but->rnaprop) { + return; + } - if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { - return; - } + if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { + return; + } - buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); - if (buf) { - BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); - MEM_freeN(buf); + /* make a little report about what we've done! */ + char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); + if (buf) { + BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); + MEM_freeN(buf); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL); - } + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL); } } @@ -1631,29 +1635,34 @@ static bool ui_drag_toggle_set_xy_xy( LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* NOTE: ctrl is always true here because (at least for now) * we always want to consider text control in this case, even when not embossed. */ - if (ui_but_is_interactive(but, true)) { - if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { - - /* execute the button */ - if (ui_drag_toggle_but_is_supported(but)) { - /* is it pressed? */ - const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); - if (pushed_state_but != pushed_state) { - UI_but_execute(C, region, but); - if (do_check) { - ui_but_update_edited(but); - } - if (U.runtime.is_dirty == false) { - ui_but_update_preferences_dirty(but); - } - changed = true; - } - } - /* done */ - } + + if (!ui_but_is_interactive(but, true)) { + continue; + } + if (!BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { + continue; + } + if (!ui_drag_toggle_but_is_supported(but)) { + continue; } + /* is it pressed? */ + const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); + if (pushed_state_but == pushed_state) { + continue; + } + + /* execute the button */ + UI_but_execute(C, region, but); + if (do_check) { + ui_but_update_edited(but); + } + if (U.runtime.is_dirty == false) { + ui_but_update_preferences_dirty(but); + } + changed = true; } } + if (changed) { /* apply now, not on release (or if handlers are canceled for whatever reason) */ ui_apply_but_funcs_after(C); @@ -4514,7 +4523,8 @@ static int ui_do_but_HOTKEYEVT(bContext *C, BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY, EVT_BUT_OPEN) && + (event->val == KM_PRESS)) { but->drawstr[0] = 0; hotkey_but->modifier_key = 0; button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); @@ -4537,13 +4547,9 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) { - /* data->cancel doesn't work, this button opens immediate */ - if (but->flag & UI_BUT_IMMEDIATE) { - ui_but_value_set(but, 0); - } - else { - data->cancel = true; - } + data->cancel = true; + /* Close the containing popup (if any). */ + data->escapecancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } @@ -6328,7 +6334,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); - IMB_colormanagement_srgb_to_scene_linear_v3(target); + IMB_colormanagement_srgb_to_scene_linear_v3(target, target); } else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); @@ -6345,7 +6351,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co } else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); - IMB_colormanagement_scene_linear_to_srgb_v3(color); + IMB_colormanagement_scene_linear_to_srgb_v3(color, color); BKE_brush_color_set(scene, brush, color); updated = true; } @@ -8552,14 +8558,6 @@ static void button_activate_init(bContext *C, } button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); - /* activate right away */ - if (but->flag & UI_BUT_IMMEDIATE) { - if (but->type == UI_BTYPE_HOTKEY_EVENT) { - button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); - } - /* .. more to be added here */ - } - if (type == BUTTON_ACTIVATE_OPEN) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index a79d96caeca..4221959a430 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1225,24 +1225,24 @@ typedef enum { /** * Helper call to draw a menu item without a button. * - * \param state: The state of the button, - * typically #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. + * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically + * #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. * \param separator_type: The kind of separator which controls if and how the string is clipped. - * \param r_xmax: The right hand position of the text, this takes into the icon, - * padding and text clipping when there is not enough room to display the full text. + * \param r_xmax: The right hand position of the text, this takes into the icon, padding and text + * clipping when there is not enough room to display the full text. */ void ui_draw_menu_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, uiMenuItemSeparatorType separator_type, int *r_xmax); void ui_draw_preview_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, eFontStyle_Align text_align); /** * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 19b425d585b..0df62c8f082 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1107,7 +1107,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, NULL); UI_but_func_set(but, ui_keymap_but_cb, but, NULL); if (flag & UI_ITEM_R_IMMEDIATE) { - UI_but_flag_enable(but, UI_BUT_IMMEDIATE); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); } } else { @@ -2338,7 +2338,14 @@ void uiItemFullR(uiLayout *layout, /* property with separate label */ else if (ELEM(type, PROP_ENUM, PROP_STRING, PROP_POINTER)) { but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag); - but = ui_but_add_search(but, ptr, prop, NULL, NULL, false); + bool results_are_suggestions = false; + if (type == PROP_STRING) { + const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(prop); + if (search_flag & PROP_STRING_SEARCH_SUGGESTION) { + results_are_suggestions = true; + } + } + but = ui_but_add_search(but, ptr, prop, NULL, NULL, results_are_suggestions); if (layout->redalert) { UI_but_flag_enable(but, UI_BUT_REDALERT); @@ -2711,11 +2718,16 @@ uiBut *ui_but_add_search(uiBut *but, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, - bool results_are_suggestions) + const bool results_are_suggestions) { /* for ID's we do automatic lookup */ + bool has_search_fn = false; + PointerRNA sptr; if (!searchprop) { + if (RNA_property_type(prop) == PROP_STRING) { + has_search_fn = (RNA_property_string_search_flag(prop) != 0); + } if (RNA_property_type(prop) == PROP_POINTER) { StructRNA *ptype = RNA_property_pointer_type(ptr, prop); search_id_collection(ptype, &sptr, &searchprop); @@ -2724,14 +2736,18 @@ uiBut *ui_but_add_search(uiBut *but, } /* turn button into search button */ - if (searchprop) { + if (has_search_fn || searchprop) { uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__); uiButSearch *search_but; but = ui_but_change_type(but, UI_BTYPE_SEARCH_MENU); search_but = (uiButSearch *)but; - search_but->rnasearchpoin = *searchptr; - search_but->rnasearchprop = searchprop; + + if (searchptr) { + search_but->rnasearchpoin = *searchptr; + search_but->rnasearchprop = searchprop; + } + but->hardmax = MAX2(but->hardmax, 256.0f); but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT; if (RNA_property_is_unlink(prop)) { @@ -2740,8 +2756,17 @@ uiBut *ui_but_add_search(uiBut *but, coll_search->target_ptr = *ptr; coll_search->target_prop = prop; - coll_search->search_ptr = *searchptr; - coll_search->search_prop = searchprop; + + if (searchptr) { + coll_search->search_ptr = *searchptr; + coll_search->search_prop = searchprop; + } + else { + /* Rely on `has_search_fn`. */ + coll_search->search_ptr = PointerRNA_NULL; + coll_search->search_prop = NULL; + } + coll_search->search_but = but; coll_search->butstore_block = but->block; coll_search->butstore = UI_butstore_create(coll_search->butstore_block); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 5b97a80d513..c066ced21cb 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1895,14 +1895,14 @@ static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { if (!gamma) { - IMB_colormanagement_scene_linear_to_srgb_v3(color); + IMB_colormanagement_scene_linear_to_srgb_v3(color, color); } RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); RNA_property_update(C, &but->rnapoin, but->rnaprop); } else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) { if (gamma) { - IMB_colormanagement_srgb_to_scene_linear_v3(color); + IMB_colormanagement_srgb_to_scene_linear_v3(color, color); } RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); RNA_property_update(C, &but->rnapoin, but->rnaprop); @@ -2142,11 +2142,8 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - if (!RNA_struct_property_is_set(op->ptr, "session_uuid")) { - return OPERATOR_CANCELLED; - } - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - Material *ma = (Material *)BKE_libblock_find_session_uuid(bmain, ID_MA, session_uuid); + Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_MA); if (ma == NULL) { return OPERATOR_CANCELLED; } @@ -2184,16 +2181,7 @@ static void UI_OT_drop_material(wmOperatorType *ot) ot->exec = ui_drop_material_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - PropertyRNA *prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + WM_operator_properties_id_lookup(ot, false); } /** \} */ diff --git a/source/blender/editors/interface/interface_region_color_picker.cc b/source/blender/editors/interface/interface_region_color_picker.cc index ab0a6039cdc..db1e5e653de 100644 --- a/source/blender/editors/interface/interface_region_color_picker.cc +++ b/source/blender/editors/interface/interface_region_color_picker.cc @@ -116,7 +116,7 @@ void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3]) * assuming it is more perceptually linear than the scene linear * space for intuitive color picking. */ if (!ui_but_is_color_gamma(but)) { - IMB_colormanagement_scene_linear_to_color_picking_v3(rgb); + IMB_colormanagement_scene_linear_to_color_picking_v3(rgb, rgb); ui_color_picker_rgb_round(rgb); } } @@ -124,7 +124,7 @@ void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3]) void ui_perceptual_to_scene_linear_space(uiBut *but, float rgb[3]) { if (!ui_but_is_color_gamma(but)) { - IMB_colormanagement_color_picking_to_scene_linear_v3(rgb); + IMB_colormanagement_color_picking_to_scene_linear_v3(rgb, rgb); ui_color_picker_rgb_round(rgb); } } @@ -208,7 +208,7 @@ static void ui_update_color_picker_buts_rgb(uiBut *from_but, * (coming from other applications, web, etc) */ copy_v3_v3(rgb_hex, rgb_scene_linear); if (from_but && !ui_but_is_color_gamma(from_but)) { - IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex); + IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex, rgb_hex); ui_color_picker_rgb_round(rgb_hex); } @@ -291,7 +291,7 @@ static void ui_colorpicker_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexc /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */ if (!ui_but_is_color_gamma(but)) { - IMB_colormanagement_srgb_to_scene_linear_v3(rgb); + IMB_colormanagement_srgb_to_scene_linear_v3(rgb, rgb); ui_color_picker_rgb_round(rgb); } @@ -777,7 +777,7 @@ static void ui_block_colorpicker(uiBlock *block, copy_v3_v3(rgb_hex, rgba_scene_linear); if (!ui_but_is_color_gamma(from_but)) { - IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex); + IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex, rgb_hex); ui_color_picker_rgb_round(rgb_hex); } diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index bc497e2647c..64de31dfe6a 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -58,7 +58,7 @@ struct uiSearchItems { char **names; void **pointers; int *icons; - int *states; + int *but_flags; uint8_t *name_prefix_offsets; /** Is there any item with an icon? */ @@ -94,7 +94,7 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + const int but_flag, const uint8_t name_prefix_offset) { /* hijack for autocomplete */ @@ -148,10 +148,10 @@ bool UI_search_item_add(uiSearchItems *items, /* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set * which will cause problems, add others as needed. */ - BLI_assert( - (state & ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0); - if (items->states) { - items->states[items->totitem] = state; + BLI_assert((but_flag & + ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0); + if (items->but_flags) { + items->but_flags[items->totitem] = but_flag; } items->totitem++; @@ -556,7 +556,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) if (data->preview) { /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; /* ensure icon is up-to-date */ ui_icon_ensure_deferred(C, data->items.icons[a], data->preview); @@ -568,7 +568,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) &rect, data->items.names[a], data->items.icons[a], - state, + but_flag, UI_STYLE_TEXT_LEFT); } @@ -590,7 +590,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0; /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; char *name = data->items.names[a]; int icon = data->items.icons[a]; char *name_sep_test = nullptr; @@ -600,7 +600,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT; } /* Only set for displaying additional hint (e.g. library name of a linked data-block). */ - else if (state & UI_BUT_HAS_SEP_CHAR) { + else if (but_flag & UI_BUT_HAS_SEP_CHAR) { separator_type = UI_MENU_ITEM_SEPARATOR_HINT; } @@ -615,7 +615,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) } /* Simple menu item. */ - ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, separator_type, nullptr); + ui_draw_menu_item(&data->fstyle, &rect, name, icon, but_flag, separator_type, nullptr); } else { /* Split menu item, faded text before the separator. */ @@ -633,7 +633,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) &rect, name, 0, - state | UI_BUT_INACTIVE, + but_flag | UI_BUT_INACTIVE, UI_MENU_ITEM_SEPARATOR_NONE, &name_width); *name_sep = name_sep_prev; @@ -646,7 +646,8 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) } /* The previous menu item draws the active selection. */ - ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, nullptr); + ui_draw_menu_item( + &data->fstyle, &rect, name_sep, icon, but_flag, separator_type, nullptr); } } /* indicate more */ @@ -677,7 +678,7 @@ static void ui_searchbox_region_free_fn(ARegion *region) MEM_freeN(data->items.names); MEM_freeN(data->items.pointers); MEM_freeN(data->items.icons); - MEM_freeN(data->items.states); + MEM_freeN(data->items.but_flags); if (data->items.name_prefix_offsets != nullptr) { MEM_freeN(data->items.name_prefix_offsets); @@ -847,7 +848,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.names = (char **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.pointers = (void **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.icons = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); - data->items.states = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); + data->items.but_flags = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); data->items.name_prefix_offsets = nullptr; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { data->items.names[i] = (char *)MEM_callocN(data->items.maxstrlen + 1, __func__); @@ -913,7 +914,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* widget itself */ /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */ { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; wmOperatorType *ot = static_cast<wmOperatorType *>(data->items.pointers[a]); char text_pre[128]; @@ -936,14 +937,14 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe &rect_pre, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre), data->items.icons[a], - state, + but_flag, UI_MENU_ITEM_SEPARATOR_NONE, nullptr); ui_draw_menu_item(&data->fstyle, &rect_post, data->items.names[a], 0, - state, + but_flag, data->use_shortcut_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT : UI_MENU_ITEM_SEPARATOR_NONE, nullptr); diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 384e9d1794e..dc8f568d025 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -40,8 +40,8 @@ static StringRef attribute_domain_string(const AttributeDomain domain) static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) { - const StringRef data_type_name = attribute_data_type_string(item.data_type); - const StringRef domain_name = attribute_domain_string(item.domain); + 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 + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + data_type_name; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 4e6437e043a..a02e8a3ac49 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -742,7 +742,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, object_active->id.tag |= LIB_TAG_DOIT; } BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); } else if (object_active != NULL && !ID_IS_LINKED(object_active) && &object_active->instance_collection->id == id) { @@ -754,7 +754,8 @@ static void template_id_liboverride_hierarchy_create(bContext *C, id, &object_active->id, &object_active->id, - &id_override); + &id_override, + false); } break; case ID_OB: @@ -765,7 +766,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, object_active->id.tag |= LIB_TAG_DOIT; } BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); } break; case ID_ME: @@ -787,13 +788,20 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + false); } else { object_active->id.tag |= LIB_TAG_DOIT; BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false); } } break; @@ -812,8 +820,11 @@ static void template_id_liboverride_hierarchy_create(bContext *C, id_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; *r_undo_push_label = "Make Library Override Hierarchy"; - WM_event_add_notifier(C, NC_WINDOW, NULL); - DEG_relations_tag_update(bmain); + /* Given `idptr` is re-assigned to owner property by caller to ensure proper updates etc. Here + * we also use it to ensure remapping of the owner property from the linked data to the newly + * created liboverride (note that in theory this remapping has already been done by code + * above). */ + RNA_id_pointer_create(id_override, idptr); } } diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index 993ccdf92f7..b7ca2d9aa11 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -24,6 +24,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -516,71 +517,136 @@ void ui_rna_collection_search_update_fn( StringSearch *search = skip_filter ? nullptr : BLI_string_search_new(); - /* build a temporary list of relevant items first */ - int item_index = 0; - RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { + if (data->search_prop != nullptr) { + /* build a temporary list of relevant items first */ + int item_index = 0; + RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { - if (flag & PROP_ID_SELF_CHECK) { - if (itemptr.data == data->target_ptr.owner_id) { - continue; + if (flag & PROP_ID_SELF_CHECK) { + if (itemptr.data == data->target_ptr.owner_id) { + continue; + } } - } - /* use filter */ - if (is_ptr_target) { - if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) { - continue; + /* use filter */ + if (is_ptr_target) { + if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) { + continue; + } } - } - int name_prefix_offset = 0; - int iconid = ICON_NONE; - bool has_sep_char = false; - const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); + int name_prefix_offset = 0; + int iconid = ICON_NONE; + bool has_sep_char = false; + const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); - if (is_id) { - iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false); - if (!ELEM(iconid, 0, ICON_BLANK1)) { - has_id_icon = true; - } + if (is_id) { + iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false); + if (!ELEM(iconid, 0, ICON_BLANK1)) { + has_id_icon = true; + } - if (requires_exact_data_name) { - name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); + if (requires_exact_data_name) { + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); + } + else { + const ID *id = static_cast<ID *>(itemptr.data); + BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset); + BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, + "Name string buffer should be big enough to hold full UI ID name"); + name = name_buf; + has_sep_char = ID_IS_LINKED(id); + } } else { - const ID *id = static_cast<ID *>(itemptr.data); - BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset); - BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, - "Name string buffer should be big enough to hold full UI ID name"); - name = name_buf; - has_sep_char = ID_IS_LINKED(id); + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); } - } - else { - name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); - } - if (name) { - CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__); - cis->data = itemptr.data; - cis->name = BLI_strdup(name); - cis->index = item_index; - cis->iconid = iconid; - cis->is_id = is_id; - cis->name_prefix_offset = name_prefix_offset; - cis->has_sep_char = has_sep_char; - if (!skip_filter) { - BLI_string_search_add(search, name, cis, 0); + if (name) { + CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__); + cis->data = itemptr.data; + cis->name = BLI_strdup(name); + cis->index = item_index; + cis->iconid = iconid; + cis->is_id = is_id; + cis->name_prefix_offset = name_prefix_offset; + cis->has_sep_char = has_sep_char; + if (!skip_filter) { + BLI_string_search_add(search, name, cis, 0); + } + BLI_addtail(items_list, cis); + if (name != name_buf) { + MEM_freeN(name); + } } - BLI_addtail(items_list, cis); - if (name != name_buf) { - MEM_freeN(name); + + item_index++; + } + RNA_PROP_END; + } + else { + BLI_assert(RNA_property_type(data->target_prop) == PROP_STRING); + const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag( + data->target_prop); + BLI_assert(search_flag & PROP_STRING_SEARCH_SUPPORTED); + + struct SearchVisitUserData { + StringSearch *search; + bool skip_filter; + int item_index; + ListBase *items_list; + const char *func_id; + } user_data = {nullptr}; + + user_data.search = search; + user_data.skip_filter = skip_filter; + user_data.items_list = items_list; + user_data.func_id = __func__; + + RNA_property_string_search( + C, + &data->target_ptr, + data->target_prop, + str, + [](void *user_data, const StringPropertySearchVisitParams *visit_params) { + const bool show_extra_info = (G.debug_value == 102); + + SearchVisitUserData *search_data = (struct SearchVisitUserData *)user_data; + CollItemSearch *cis = MEM_cnew<CollItemSearch>(search_data->func_id); + cis->data = nullptr; + if (visit_params->info && show_extra_info) { + cis->name = BLI_sprintfN( + "%s" UI_SEP_CHAR_S "%s", visit_params->text, visit_params->info); + } + else { + cis->name = BLI_strdup(visit_params->text); + } + cis->index = search_data->item_index; + cis->iconid = ICON_NONE; + cis->is_id = false; + cis->name_prefix_offset = 0; + cis->has_sep_char = visit_params->info != nullptr; + if (!search_data->skip_filter) { + BLI_string_search_add(search_data->search, visit_params->text, cis, 0); + } + BLI_addtail(search_data->items_list, cis); + search_data->item_index++; + }, + (void *)&user_data); + + if (search_flag & PROP_STRING_SEARCH_SORT) { + BLI_listbase_sort(items_list, [](const void *a_, const void *b_) -> int { + const CollItemSearch *cis_a = (const CollItemSearch *)a_; + const CollItemSearch *cis_b = (const CollItemSearch *)b_; + return BLI_strcasecmp_natural(cis_a->name, cis_b->name); + }); + int i = 0; + LISTBASE_FOREACH (CollItemSearch *, cis, items_list) { + cis->index = i; + i++; } } - - item_index++; } - RNA_PROP_END; if (skip_filter) { LISTBASE_FOREACH (CollItemSearch *, cis, items_list) { diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 1c9cf27fd09..110f8146c7f 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -108,22 +108,22 @@ typedef enum { UI_WTYPE_GRID_TILE, } uiWidgetTypeEnum; -/* Button state argument shares bits with 'uiBut.flag'. - * reuse flags that aren't needed for drawing to avoid collision. */ -enum { - /* Show that holding the button opens a menu. */ - UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY, - UI_STATE_TEXT_INPUT = UI_BUT_UNDO, - UI_STATE_ACTIVE_LEFT = UI_BUT_VALUE_CLEAR, - UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE, - UI_STATE_TEXT_BEFORE_WIDGET = UI_BUT_IMMEDIATE, - - UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT | UI_STATE_ACTIVE_LEFT | - UI_STATE_ACTIVE_RIGHT | UI_STATE_TEXT_BEFORE_WIDGET), -}; -/* Prevent accidental use. */ -#define UI_BUT_UPDATE_DELAY ((void)0) -#define UI_BUT_UNDO ((void)0) +/** + * The button's state information adapted for drawing. Use #STATE_INFO_NULL for empty state. + */ +typedef struct { + /** Copy of #uiBut.flag (possibly with overrides for drawing). */ + int but_flag; + /** Copy of #uiBut.drawflag (possibly with overrides for drawing). */ + int but_drawflag; + + /** Show that holding the button opens a menu. */ + bool has_hold_action : 1; + /** The button is in text input mode. */ + bool is_text_input : 1; +} uiWidgetStateInfo; + +static const uiWidgetStateInfo STATE_INFO_NULL = {0}; /** \} */ @@ -257,10 +257,21 @@ typedef struct uiWidgetType { /* converted colors for state */ uiWidgetColors wcol; - void (*state)(struct uiWidgetType *, int state, int drawflag, eUIEmbossType emboss); - void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); - void (*custom)( - uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); + void (*state)(struct uiWidgetType *, const uiWidgetStateInfo *state, eUIEmbossType emboss) + ATTR_NONNULL(); + void (*draw)(uiWidgetColors *, + rcti *, + const uiWidgetStateInfo *, + int roundboxalign, + const float zoom) ATTR_NONNULL(); + void (*custom)(uiBut *, + uiWidgetColors *, + rcti *, + const uiWidgetStateInfo *, + int roundboxalign, + const float zoom) ATTR_NONNULL(); + void (*draw_block)( + uiWidgetColors *, rcti *, int block_flag, int roundboxalign, const float zoom); void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *); } uiWidgetType; @@ -1290,16 +1301,16 @@ static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol) #define PREVIEW_PAD 4 -static float widget_alpha_factor(const int state) +static float widget_alpha_factor(const uiWidgetStateInfo *state) { - if (state & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { - if (state & UI_SEARCH_FILTER_NO_MATCH) { + if (state->but_flag & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { + if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) { return 0.25f; } return 0.5f; } - if (state & UI_SEARCH_FILTER_NO_MATCH) { + if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) { return 0.5f; } @@ -1368,7 +1379,10 @@ static void widget_draw_icon( } } else if (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR)) { - alpha *= widget_alpha_factor(but->flag); + uiWidgetStateInfo state = {0}; + state.but_flag = but->flag; + state.but_drawflag = but->drawflag; + alpha *= widget_alpha_factor(&state); } GPU_blend(GPU_BLEND_ALPHA); @@ -2447,7 +2461,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, * \{ */ /* put all widget colors on half alpha, use local storage */ -static void ui_widget_color_disabled(uiWidgetType *wt, const int state) +static void ui_widget_color_disabled(uiWidgetType *wt, const uiWidgetStateInfo *state) { static uiWidgetColors wcol_theme_s; @@ -2474,8 +2488,7 @@ static void widget_active_color(uiWidgetColors *wcol) } static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state, - int state, - int drawflag, + const uiWidgetStateInfo *state, const eUIEmbossType emboss) { /* Explicitly require #UI_EMBOSS_NONE_OR_STATUS for color blending with no emboss. */ @@ -2483,44 +2496,44 @@ static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wco return NULL; } - if (drawflag & UI_BUT_ANIMATED_CHANGED) { + if (state->but_drawflag & UI_BUT_ANIMATED_CHANGED) { return wcol_state->inner_changed_sel; } - if (state & UI_BUT_ANIMATED_KEY) { + if (state->but_flag & UI_BUT_ANIMATED_KEY) { return wcol_state->inner_key_sel; } - if (state & UI_BUT_ANIMATED) { + if (state->but_flag & UI_BUT_ANIMATED) { return wcol_state->inner_anim_sel; } - if (state & UI_BUT_DRIVEN) { + if (state->but_flag & UI_BUT_DRIVEN) { return wcol_state->inner_driven_sel; } - if (state & UI_BUT_OVERRIDDEN) { + if (state->but_flag & UI_BUT_OVERRIDDEN) { return wcol_state->inner_overridden_sel; } return NULL; } /* copy colors from theme, and set changes in it based on state */ -static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state(uiWidgetType *wt, const uiWidgetStateInfo *state, eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; - if (state & UI_BUT_LIST_ITEM) { + if (state->but_flag & UI_BUT_LIST_ITEM) { /* Override default widget's colors. */ bTheme *btheme = UI_GetTheme(); wt->wcol_theme = &btheme->tui.wcol_list_item; - if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { + if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { ui_widget_color_disabled(wt, state); } } wt->wcol = *(wt->wcol_theme); - const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss); + const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); if (color_blend != NULL) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); @@ -2528,12 +2541,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); } } else { - if (state & UI_BUT_ACTIVE_DEFAULT) { + if (state->but_flag & UI_BUT_ACTIVE_DEFAULT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel); } @@ -2545,12 +2558,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp * even if UI_SELECT. But currently this causes some flickering * as buttons can be created and updated without respect to mouse * position and so can draw without UI_ACTIVE set. See D6503. */ - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { widget_active_color(&wt->wcol); } } - if (state & UI_BUT_REDALERT) { + if (state->but_flag & UI_BUT_REDALERT) { const uchar red[4] = {255, 0, 0}; if (wt->draw && emboss != UI_EMBOSS_NONE) { color_blend_v3_v3(wt->wcol.inner, red, 0.4f); @@ -2560,14 +2573,14 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp } } - if (state & UI_BUT_DRAG_MULTI) { + if (state->but_flag & UI_BUT_DRAG_MULTI) { /* the button isn't SELECT but we're editing this so draw with sel color */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.85f); } - if (state & UI_BUT_NODE_ACTIVE) { + if (state->but_flag & UI_BUT_NODE_ACTIVE) { const uchar blue[4] = {86, 128, 194}; color_blend_v3_v3(wt->wcol.inner, blue, 0.3f); } @@ -2601,14 +2614,16 @@ static float widget_radius_from_rcti(const rcti *rect, const uiWidgetColors *wco * \{ */ /* sliders use special hack which sets 'item' as inner when drawing filling */ -static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_numslider(uiWidgetType *wt, + const uiWidgetStateInfo *state, + eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; /* call this for option button */ - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); - const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss); + const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss); if (color_blend != NULL) { /* Set the slider 'item' so that it reflects state settings too. * De-saturate so the color of the slider doesn't conflict with the blend color, @@ -2618,15 +2633,14 @@ static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eU color_ensure_contrast_v3(wt->wcol.item, wt->wcol.inner, 30); } - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); } } /* labels use theme colors for text */ static void widget_state_option_menu(uiWidgetType *wt, - int state, - int drawflag, + const uiWidgetStateInfo *state, eUIEmbossType emboss) { const bTheme *btheme = UI_GetTheme(); @@ -2639,14 +2653,13 @@ static void widget_state_option_menu(uiWidgetType *wt, copy_v3_v3_uchar(wcol_menu_option.text_sel, btheme->tui.wcol_menu_back.text_sel); wt->wcol_theme = &wcol_menu_option; - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); wt->wcol_theme = old_wcol; } static void widget_state_nothing(uiWidgetType *wt, - int UNUSED(state), - int UNUSED(drawflag), + const uiWidgetStateInfo *UNUSED(state), eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); @@ -2654,8 +2667,7 @@ static void widget_state_nothing(uiWidgetType *wt, /* special case, button that calls pulldown */ static void widget_state_pulldown(uiWidgetType *wt, - int UNUSED(state), - int UNUSED(drawflag), + const uiWidgetStateInfo *UNUSED(state), eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); @@ -2663,14 +2675,13 @@ static void widget_state_pulldown(uiWidgetType *wt, /* special case, pie menu items */ static void widget_state_pie_menu_item(uiWidgetType *wt, - int state, - int UNUSED(drawflag), + const uiWidgetStateInfo *state, eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); /* active and disabled (not so common) */ - if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) { color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.5f); /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ @@ -2679,18 +2690,18 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, } else { /* regular active */ - if (state & (UI_SELECT | UI_ACTIVE)) { + if (state->but_flag & (UI_SELECT | UI_ACTIVE)) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); } - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f); } - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); } - else if (state & UI_ACTIVE) { + else if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item); } } @@ -2698,14 +2709,13 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, /* special case, menu items */ static void widget_state_menu_item(uiWidgetType *wt, - int state, - int UNUSED(drawflag), + const uiWidgetStateInfo *state, eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); /* active and disabled (not so common) */ - if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) { /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ wt->wcol.text[3] = 128; @@ -2714,15 +2724,15 @@ static void widget_state_menu_item(uiWidgetType *wt, } else { /* regular active */ - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); } - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f); } - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); } } @@ -2788,7 +2798,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r } static void widget_menu_back( - uiWidgetColors *wcol, rcti *rect, int flag, int direction, const float zoom) + uiWidgetColors *wcol, rcti *rect, const int block_flag, const int direction, const float zoom) { uiWidgetBase wtb; int roundboxalign = UI_CNR_ALL; @@ -2796,7 +2806,7 @@ static void widget_menu_back( widget_init(&wtb); /* menu is 2nd level or deeper */ - if (flag & UI_BLOCK_POPUP) { + if (block_flag & UI_BLOCK_POPUP) { // rect->ymin -= 4.0; // rect->ymax += 4.0; } @@ -3322,13 +3332,17 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) #define NUM_BUT_PADDING_FACTOR 0.425f -static void widget_numbut_draw( - uiWidgetColors *wcol, rcti *rect, const float zoom, int state, int roundboxalign, bool emboss) +static void widget_numbut_draw(uiWidgetColors *wcol, + rcti *rect, + const float zoom, + const uiWidgetStateInfo *state, + int roundboxalign, + bool emboss) { const float rad = widget_radius_from_zoom(zoom, wcol); const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3344,7 +3358,7 @@ static void widget_numbut_draw( } /* decoration */ - if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) { + if ((state->but_flag & UI_ACTIVE) && !state->is_text_input) { uiWidgetColors wcol_zone; uiWidgetBase wtb_zone; rcti rect_zone; @@ -3357,7 +3371,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (state & UI_STATE_ACTIVE_LEFT) { + if (state->but_drawflag & UI_BUT_ACTIVE_LEFT) { widget_active_color(&wcol_zone); } @@ -3377,7 +3391,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (state & UI_STATE_ACTIVE_RIGHT) { + if (state->but_drawflag & UI_BUT_ACTIVE_RIGHT) { widget_active_color(&wcol_zone); } @@ -3396,7 +3410,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) { + if (!(state->but_drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT))) { widget_active_color(&wcol_zone); } @@ -3415,7 +3429,7 @@ static void widget_numbut_draw( widgetbase_draw(&wtb, wcol); } - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); rect->xmin += text_padding; @@ -3423,14 +3437,20 @@ static void widget_numbut_draw( } } -static void widget_numbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { widget_numbut_draw(wcol, rect, zoom, state, roundboxalign, false); } -static void widget_menubut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_menubut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3455,7 +3475,7 @@ static void widget_menubut( static void widget_menubut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; @@ -3477,7 +3497,7 @@ static void widget_menubut_embossn(const uiBut *UNUSED(but), static void widget_numbut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int roundboxalign, const float zoom) { @@ -3487,7 +3507,6 @@ static void widget_numbut_embossn(const uiBut *UNUSED(but), void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state) { uiWidgetBase wtb; - bool outline = false; widget_init(&wtb); @@ -3532,11 +3551,6 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s /* draw */ wtb.draw_emboss = false; /* only emboss once */ - /* exception for progress bar */ - if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.draw_outline); - } - round_box_edges(&wtb, UI_CNR_ALL, slider, rad); if (state & UI_SCROLL_ARROWS) { @@ -3564,17 +3578,13 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s } } widgetbase_draw(&wtb, wcol); - - if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.draw_outline); - } } } static void widget_scroll(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3624,19 +3634,13 @@ static void widget_scroll(uiBut *but, } } - if (state & UI_SELECT) { - state = UI_SCROLL_PRESSED; - } - else { - state = 0; - } - UI_draw_widget_scroll(wcol, rect, &rect1, state); + UI_draw_widget_scroll(wcol, rect, &rect1, (state->but_flag & UI_SELECT) ? UI_SCROLL_PRESSED : 0); } static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int roundboxalign, const float zoom) { @@ -3670,7 +3674,7 @@ static void widget_progressbar(uiBut *but, static void widget_treerow_exec(uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), int indentation, const float zoom) @@ -3683,7 +3687,7 @@ static void widget_treerow_exec(uiWidgetColors *wcol, const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); - if ((state & UI_ACTIVE) || (state & UI_SELECT)) { + if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) { widgetbase_draw(&wtb, wcol); } @@ -3691,8 +3695,12 @@ static void widget_treerow_exec(uiWidgetColors *wcol, BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); } -static void widget_treerow( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_treerow(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiButTreeRow *tree_row = (uiButTreeRow *)but; BLI_assert(but->type == UI_BTYPE_TREEROW); @@ -3709,7 +3717,7 @@ static void widget_gridtile( static void widget_nodesocket(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3745,8 +3753,12 @@ static void widget_nodesocket(uiBut *but, copy_v3_v3_uchar(wcol->outline, old_outline); } -static void widget_numslider( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numslider(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb, wtb1; widget_init(&wtb); @@ -3760,7 +3772,7 @@ static void widget_numslider( widgetbase_draw(&wtb, wcol); /* Draw slider part only when not in text editing. */ - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { int roundboxalign_slider = roundboxalign; uchar outline[3]; @@ -3768,7 +3780,7 @@ static void widget_numslider( copy_v3_v3_uchar(wcol->outline, wcol->item); copy_v3_v3_uchar(wcol->inner, wcol->item); - if (!(state & UI_SELECT)) { + if (!(state->but_flag & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3836,7 +3848,7 @@ static void widget_numslider( copy_v3_v3_uchar(wcol->outline, outline); - if (!(state & UI_SELECT)) { + if (!(state->but_flag & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); } } @@ -3848,7 +3860,7 @@ static void widget_numslider( /* Add space at either side of the button so text aligns with number-buttons * (which have arrow icons). */ - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); rect->xmax -= text_padding; rect->xmin += text_padding; @@ -3858,8 +3870,12 @@ static void widget_numslider( /* I think 3 is sufficient border to indicate keyed status */ #define SWATCH_KEYED_BORDER 3 -static void widget_swatch( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_swatch(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { BLI_assert(but->type == UI_BTYPE_COLOR); uiButColor *color_but = (uiButColor *)but; @@ -3883,9 +3899,9 @@ static void widget_swatch( ui_but_v3_get(but, col); - if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDDEN | - UI_BUT_REDALERT)) || - (but->drawflag & UI_BUT_ANIMATED_CHANGED)) { + if ((state->but_flag & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | + UI_BUT_OVERRIDDEN | UI_BUT_REDALERT)) || + (state->but_drawflag & UI_BUT_ANIMATED_CHANGED)) { /* draw based on state - color for keyed etc */ widgetbase_draw(&wtb, wcol); @@ -3946,7 +3962,7 @@ static void widget_swatch( static void widget_unitvec(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -3954,10 +3970,15 @@ static void widget_unitvec(uiBut *but, ui_draw_but_UNITVEC(but, wcol, rect, rad); } -static void widget_icon_has_anim( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_icon_has_anim(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { - if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && + if (state->but_flag & + (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && but->emboss != UI_EMBOSS_NONE) { uiWidgetBase wtb; widget_init(&wtb); @@ -3978,10 +3999,13 @@ static void widget_icon_has_anim( } } -static void widget_textbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_textbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3997,7 +4021,7 @@ static void widget_textbut( static void widget_preview_tile(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -4006,8 +4030,11 @@ static void widget_preview_tile(uiBut *but, &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); } -static void widget_menuiconbut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_menuiconbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4019,17 +4046,20 @@ static void widget_menuiconbut( widgetbase_draw(&wtb, wcol); } -static void widget_pulldownbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_pulldownbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { float back[4]; UI_GetThemeColor4fv(TH_BACK, back); - if ((state & UI_ACTIVE) || (back[3] < 1.0f)) { + if ((state->but_flag & UI_ACTIVE) || (back[3] < 1.0f)) { uiWidgetBase wtb; const float rad = widget_radius_from_zoom(zoom, wcol); - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wcol->inner, wcol->inner_sel); copy_v3_v3_uchar(wcol->text, wcol->text_sel); copy_v3_v3_uchar(wcol->outline, wcol->inner); @@ -4050,7 +4080,7 @@ static void widget_pulldownbut( static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4074,7 +4104,7 @@ static void widget_menu_itembut(uiWidgetColors *wcol, static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4096,7 +4126,7 @@ static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4122,7 +4152,7 @@ static void widget_menu_radial_itembut(uiBut *but, static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4139,11 +4169,13 @@ static void widget_list_itembut(uiWidgetColors *wcol, static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { - const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); + /* For a right aligned layout (signified by #UI_BUT_TEXT_RIGHT), draw the text on the left of the + * checkbox. */ + const bool text_before_widget = (state->but_drawflag & UI_BUT_TEXT_RIGHT); rcti recttemp = *rect; uiWidgetBase wtb; @@ -4168,7 +4200,7 @@ static void widget_optionbut(uiWidgetColors *wcol, round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad); /* decoration */ - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp); } @@ -4185,19 +4217,21 @@ static void widget_optionbut(uiWidgetColors *wcol, } /* labels use Editor theme colors for text */ -static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_label(uiWidgetType *wt, + const uiWidgetStateInfo *state, + eUIEmbossType emboss) { - if (state & UI_BUT_LIST_ITEM) { + if (state->but_flag & UI_BUT_LIST_ITEM) { /* Override default label theme's colors. */ bTheme *btheme = UI_GetTheme(); wt->wcol_theme = &btheme->tui.wcol_list_item; /* call this for option button */ - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); } else { /* call this for option button */ - widget_state(wt, state, drawflag, emboss); - if (state & UI_SELECT) { + widget_state(wt, state, emboss); + if (state->but_flag & UI_SELECT) { UI_GetThemeColor3ubv(TH_TEXT_HI, wt->wcol.text); } else { @@ -4205,14 +4239,17 @@ static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmb } } - if (state & UI_BUT_REDALERT) { + if (state->but_flag & UI_BUT_REDALERT) { const uchar red[4] = {255, 0, 0}; color_blend_v3_v3(wt->wcol.text, red, 0.4f); } } -static void widget_radiobut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_radiobut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4226,7 +4263,7 @@ static void widget_radiobut( static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int roundboxalign, const float zoom) { @@ -4252,8 +4289,11 @@ static void widget_box(uiBut *but, copy_v3_v3_uchar(wcol->inner, old_col); } -static void widget_but( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_but(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4279,13 +4319,16 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), } #endif -static void widget_roundbut_exec( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_roundbut_exec(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); - if (state & UI_STATE_HOLD_ACTION) { + if (state->has_hold_action) { /* Show that keeping pressed performs another action (typically a menu). */ shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r'); } @@ -4298,11 +4341,14 @@ static void widget_roundbut_exec( widgetbase_draw(&wtb, wcol); } -static void widget_tab( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_tab(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { const float rad = widget_radius_from_zoom(zoom, wcol); - const bool is_active = (state & UI_SELECT); + const bool is_active = (state->but_flag & UI_SELECT); /* Draw shaded outline - Disabled for now, * seems incorrect and also looks nicer without it IMHO ;). */ @@ -4450,7 +4496,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_TOOLTIP: wt.wcol_theme = &btheme->tui.wcol_tooltip; - wt.draw = widget_menu_back; + wt.draw_block = widget_menu_back; break; /* strings */ @@ -4506,7 +4552,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_MENU_BACK: wt.wcol_theme = &btheme->tui.wcol_menu_back; - wt.draw = widget_menu_back; + wt.draw_block = widget_menu_back; break; /* specials */ @@ -4940,62 +4986,50 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu const int roundboxalign = widget_roundbox_set(but, rect); - /* Mask out flags re-used for local state. */ - int state = but->flag & ~UI_STATE_FLAGS_ALL; - const int drawflag = but->drawflag; + uiWidgetStateInfo state = {0}; + state.but_flag = but->flag; + state.but_drawflag = but->drawflag; - if (state & UI_SELECT_DRAW) { - state |= UI_SELECT; + /* Override selected flag for drawing. */ + if (but->flag & UI_SELECT_DRAW) { + state.but_flag |= UI_SELECT; } if ((but->editstr) || (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) { - state |= UI_STATE_TEXT_INPUT; + state.is_text_input = true; } if (but->hold_func) { - state |= UI_STATE_HOLD_ACTION; - } - - if (state & UI_ACTIVE) { - if (but->drawflag & UI_BUT_ACTIVE_LEFT) { - state |= UI_STATE_ACTIVE_LEFT; - } - else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { - state |= UI_STATE_ACTIVE_RIGHT; - } + state.has_hold_action = true; } bool use_alpha_blend = false; if (but->emboss != UI_EMBOSS_PULLDOWN) { - if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { + if (but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { use_alpha_blend = true; - ui_widget_color_disabled(wt, state); + ui_widget_color_disabled(wt, &state); } } - if (drawflag & UI_BUT_TEXT_RIGHT) { - state |= UI_STATE_TEXT_BEFORE_WIDGET; - } - #ifdef USE_UI_POPOVER_ONCE if (but->block->flag & UI_BLOCK_POPOVER_ONCE) { - if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { - state |= UI_BUT_ACTIVE_DEFAULT; + if ((but->flag & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { + state.but_flag |= UI_BUT_ACTIVE_DEFAULT; } } #endif if (but->block->flag & UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE) { - state &= ~UI_BUT_OVERRIDDEN; + state.but_flag &= ~UI_BUT_OVERRIDDEN; } const float zoom = 1.0f / but->block->aspect; - wt->state(wt, state, drawflag, but->emboss); + wt->state(wt, &state, but->emboss); if (wt->custom) { - wt->custom(but, &wt->wcol, rect, state, roundboxalign, zoom); + wt->custom(but, &wt->wcol, rect, &state, roundboxalign, zoom); } else if (wt->draw) { - wt->draw(&wt->wcol, rect, state, roundboxalign, zoom); + wt->draw(&wt->wcol, rect, &state, roundboxalign, zoom); } if (wt->text) { @@ -5036,13 +5070,13 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK); - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); if (block) { const float zoom = 1.0f / block->aspect; - wt->draw(&wt->wcol, rect, block->flag, block->direction, zoom); + wt->draw_block(&wt->wcol, rect, block->flag, block->direction, zoom); } else { - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f); } ui_draw_clip_tri(block, rect, wt); @@ -5137,8 +5171,8 @@ void ui_draw_popover_back(struct ARegion *region, } else { const float zoom = 1.0f / block->aspect; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, zoom); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); + wt->draw_block(&wt->wcol, rect, 0, 0, zoom); } ui_draw_clip_tri(block, rect, wt); @@ -5326,11 +5360,20 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type, } rcti rect_copy = *rect; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); if (color) { rgba_float_to_uchar(wt->wcol.inner, color); } - wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f); + + if (wt->draw_block) { + wt->draw_block(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f); + } + else if (wt->draw) { + wt->draw(&wt->wcol, &rect_copy, &STATE_INFO_NULL, UI_CNR_ALL, 1.0f); + } + else { + BLI_assert_unreachable(); + } } void ui_draw_widget_menu_back_color(const rcti *rect, bool use_shadow, const float color[4]) { @@ -5345,16 +5388,16 @@ void ui_draw_widget_menu_back(const rcti *rect, bool use_shadow) void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - /* wt->draw ends up using same function to draw the tooltip as menu_back */ - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); + /* wt->draw_block ends up using same function to draw the tooltip as menu_back */ + wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f); } void ui_draw_menu_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, uiMenuItemSeparatorType separator_type, int *r_xmax) { @@ -5365,8 +5408,11 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, int padding = 0.25f * row_height; char *cpoin = NULL; - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + uiWidgetStateInfo state = {0}; + state.but_flag = but_flag; + + wt->state(wt, &state, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f); UI_fontstyle_set(fstyle); @@ -5461,8 +5507,12 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, /* part text right aligned */ if (separator_type != UI_MENU_ITEM_SEPARATOR_NONE) { if (cpoin) { + /* State info for the hint drawing. */ + uiWidgetStateInfo hint_state = state; /* Set inactive state for grayed out text. */ - wt->state(wt, state | UI_BUT_INACTIVE, 0, UI_EMBOSS_UNDEFINED); + hint_state.but_flag |= UI_BUT_INACTIVE; + + wt->state(wt, &hint_state, UI_EMBOSS_UNDEFINED); char hint_drawstr[UI_MAX_DRAW_STR]; { @@ -5545,14 +5595,17 @@ void ui_draw_preview_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, eFontStyle_Align text_align) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM_UNPADDED); + uiWidgetStateInfo state = {0}; + state.but_flag = but_flag; + /* drawing button background */ - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->state(wt, &state, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f); ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align); } diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index 66171dc13e4..6ece7eb4ffa 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -2103,21 +2103,12 @@ void UI_view2d_text_cache_draw(ARegion *region) col_pack_prev = v2s->col.pack; } - if (v2s->rect.xmin >= v2s->rect.xmax) { - BLF_draw_default((float)(v2s->mval[0] + xofs), - (float)(v2s->mval[1] + yofs), - 0.0, - v2s->str, - BLF_DRAW_STR_DUMMY_MAX); - } - else { - BLF_enable(font_id, BLF_CLIPPING); - BLF_clipping( - font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); - BLF_draw_default( - v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX); - BLF_disable(font_id, BLF_CLIPPING); - } + BLF_enable(font_id, BLF_CLIPPING); + BLF_clipping( + font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); + BLF_draw_default( + v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX); + BLF_disable(font_id, BLF_CLIPPING); } g_v2d_strings = nullptr; diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 886586ff236..f1cd7607771 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -16,6 +16,8 @@ #include "BLT_translation.h" +#include "ED_outliner.h" + #include "MEM_guardedalloc.h" #include "RNA_access.h" @@ -410,6 +412,12 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) OBJ_import(C, &import_params); + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 0b9261eac4f..d10c420e28c 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -230,7 +230,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; - const float threshold = 9; + const float threshold = 12; float tangent[2]; float u; @@ -593,7 +593,7 @@ static int add_feather_vertex_exec(bContext *C, wmOperator *op) MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; - const float threshold = 9; + const float threshold = 12; float co[2], u; RNA_float_get_array(op->ptr, "location", co); diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index afe457a8502..89524a7b9e2 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -45,6 +45,8 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, float *r_u, float *r_score) { + const float threshold_sq = threshold * threshold; + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -139,7 +141,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } } - if (point && dist_best_sq < threshold) { + if (point && dist_best_sq < threshold_sq) { if (r_mask_layer) { *r_mask_layer = point_mask_layer; } diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index b2f33428b21..57e0d04727c 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -113,7 +113,7 @@ static int add_primitive_plane_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -178,7 +178,7 @@ static int add_primitive_cube_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -252,7 +252,7 @@ static int add_primitive_circle_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -324,7 +324,7 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -400,7 +400,7 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -476,7 +476,7 @@ static int add_primitive_grid_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -553,7 +553,7 @@ static int add_primitive_monkey_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -614,7 +614,7 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -682,7 +682,7 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index d0f37314661..f5090c0143d 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -328,7 +328,7 @@ static int add_primitive_cube_gizmo_exec(bContext *C, wmOperator *op) const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs"); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 5de7681a1e6..7972459e457 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -377,45 +377,45 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); /* Ray-cast all planes. */ + float ray_dir[3]; + float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; + float lambda_best[2] = {-FLT_MAX, FLT_MAX}; + int i; + { - float ray_dir[3]; - float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; - float lambda_best[2] = {-FLT_MAX, FLT_MAX}; - int i; + float curr_cage_adjust[3]; + float co_depth[3]; - { - float curr_cage_adjust[3]; - float co_depth[3]; + copy_v3_v3(co_depth, kcd->prev.cage); + ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); - copy_v3_v3(co_depth, kcd->prev.cage); - ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); + sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); + } - sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); + for (i = 0; i < 4; i++) { + float ray_hit[3]; + float lambda_test; + if (!isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) { + continue; } - for (i = 0; i < 4; i++) { - float ray_hit[3]; - float lambda_test; - if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) { - madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test); - if (lambda_test < 0.0f) { - if (lambda_test > lambda_best[0]) { - copy_v3_v3(ray_hit_best[0], ray_hit); - lambda_best[0] = lambda_test; - } - } - else { - if (lambda_test < lambda_best[1]) { - copy_v3_v3(ray_hit_best[1], ray_hit); - lambda_best[1] = lambda_test; - } - } + madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test); + if (lambda_test < 0.0f) { + if (lambda_test > lambda_best[0]) { + copy_v3_v3(ray_hit_best[0], ray_hit); + lambda_best[0] = lambda_test; + } + } + else { + if (lambda_test < lambda_best[1]) { + copy_v3_v3(ray_hit_best[1], ray_hit); + lambda_best[1] = lambda_test; } } - - copy_v3_v3(r_v1, ray_hit_best[0]); - copy_v3_v3(r_v2, ray_hit_best[1]); } + + copy_v3_v3(r_v1, ray_hit_best[0]); + copy_v3_v3(r_v2, ray_hit_best[1]); } static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) @@ -440,43 +440,45 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) { - if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { - float v1[3], v2[3]; + if (compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { + return; + } - /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ - knifetool_raycast_planes(kcd, v1, v2); + float v1[3], v2[3]; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ + knifetool_raycast_planes(kcd, v1, v2); - switch (kcd->constrain_axis) { - case KNF_CONSTRAIN_AXIS_X: { - immUniformColor3ubv(kcd->colors.xaxis); - break; - } - case KNF_CONSTRAIN_AXIS_Y: { - immUniformColor3ubv(kcd->colors.yaxis); - break; - } - case KNF_CONSTRAIN_AXIS_Z: { - immUniformColor3ubv(kcd->colors.zaxis); - break; - } - default: { - immUniformColor3ubv(kcd->colors.axis_extra); - break; - } + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + switch (kcd->constrain_axis) { + case KNF_CONSTRAIN_AXIS_X: { + immUniformColor3ubv(kcd->colors.xaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Y: { + immUniformColor3ubv(kcd->colors.yaxis); + break; } + case KNF_CONSTRAIN_AXIS_Z: { + immUniformColor3ubv(kcd->colors.zaxis); + break; + } + default: { + immUniformColor3ubv(kcd->colors.axis_extra); + break; + } + } - GPU_line_width(2.0); + GPU_line_width(2.0); - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, v1); - immVertex3fv(pos, v2); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); - immUnbindProgram(); - } + immUnbindProgram(); } static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) @@ -1226,7 +1228,6 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) } /* Construct BVH Tree. */ - float cos[3][3]; const float epsilon = FLT_EPSILON * 2.0f; int tottri = 0; int ob_tottri = 0; @@ -1283,8 +1284,10 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) if (!test_fn_ret) { continue; } - knife_bm_tri_cagecos_get_worldspace(kcd, b, i, cos); - BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); + + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, b, i, tri_cos); + BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3); } tottri += em->tottri; @@ -1307,6 +1310,10 @@ static void knife_bvh_raycast_cb(void *userdata, const BVHTreeRay *ray, BVHTreeRayHit *hit) { + if (index != -1) { + return; + } + KnifeTool_OpData *kcd = userdata; BMLoop **ltri; Object *ob; @@ -1315,60 +1322,49 @@ static void knife_bvh_raycast_cb(void *userdata, float dist, uv[2]; bool isect; int tottri; - float tri_cos[3][3]; - if (index != -1) { - tottri = 0; - uint b = 0; - for (; b < kcd->objects_len; b++) { - index -= tottri; - ob = kcd->objects[b]; - em = BKE_editmesh_from_object(ob); - tottri = em->tottri; - if (index < tottri) { - ltri = em->looptris[index]; - break; - } + tottri = 0; + uint b = 0; + for (; b < kcd->objects_len; b++) { + index -= tottri; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + tottri = em->tottri; + if (index < tottri) { + ltri = em->looptris[index]; + break; } + } - if (kcd->bvh.filter_cb) { - if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { - return; - } + if (kcd->bvh.filter_cb) { + if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { + return; } + } - knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); - - isect = - (ray->radius > 0.0f ? - isect_ray_tri_epsilon_v3(ray->origin, - ray->direction, - tri_cos[0], - tri_cos[1], - tri_cos[2], - &dist, - uv, - ray->radius) : + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); + isect = (ray->radius > 0.0f ? + isect_ray_tri_epsilon_v3( + ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv, ray->radius) : #ifdef USE_KDOPBVH_WATERTIGHT - isect_ray_tri_watertight_v3( - ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); + isect_ray_tri_watertight_v3( + ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, uv)); #else - isect_ray_tri_v3( - ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); + isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv); #endif - if (isect && dist < hit->dist) { - hit->dist = dist; - hit->index = index; + if (isect && dist < hit->dist) { + hit->dist = dist; + hit->index = index; - copy_v3_v3(hit->no, ltri[0]->f->no); + copy_v3_v3(hit->no, ltri[0]->f->no); - madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); - kcd->bvh.looptris = em->looptris; - copy_v2_v2(kcd->bvh.uv, uv); - kcd->bvh.base_index = b; - } + kcd->bvh.looptris = em->looptris; + copy_v2_v2(kcd->bvh.uv, uv); + kcd->bvh.base_index = b; } } @@ -1383,7 +1379,6 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, uint *r_base_index) { BMFace *face; - BMLoop **ltri; BVHTreeRayHit hit; const float dist = r_dist ? *r_dist : FLT_MAX; hit.dist = dist; @@ -1397,8 +1392,9 @@ static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, /* Hits returned in world space. */ if (r_hitout) { - ltri = kcd->bvh.looptris[hit.index]; - interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos); + interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv); if (r_cagehit) { copy_v3_v3(r_cagehit, hit.co); @@ -1434,7 +1430,6 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, kcd->bvh.filter_data = filter_userdata; BMFace *face; - BMLoop **ltri; BVHTreeRayHit hit; const float dist = r_dist ? *r_dist : FLT_MAX; hit.dist = dist; @@ -1451,8 +1446,9 @@ static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, /* Hits returned in world space. */ if (r_hitout) { - ltri = kcd->bvh.looptris[hit.index]; - interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, kcd->bvh.base_index, hit.index, tri_cos); + interp_v3_v3v3v3_uv(r_hitout, UNPACK3(tri_cos), kcd->bvh.uv); if (r_cagehit) { copy_v3_v3(r_cagehit, hit.co); @@ -1978,29 +1974,30 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) * Also remove all but one of a series of vertex hits for the same vertex. */ for (int i = 0; i < n; i++) { KnifeLineHit *lhi = &linehits[i]; - if (lhi->v) { - for (int j = i - 1; j >= 0; j--) { - KnifeLineHit *lhj = &linehits[j]; - if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || - fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { - break; - } + if (lhi->v == NULL) { + continue; + } - if (lhi->kfe == lhj->kfe) { - lhj->l = -1.0f; - is_double = true; - } + for (int j = i - 1; j >= 0; j--) { + KnifeLineHit *lhj = &linehits[j]; + if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || + fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { + break; } - for (int j = i + 1; j < n; j++) { - KnifeLineHit *lhj = &linehits[j]; - if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || - fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { - break; - } - if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) { - lhj->l = -1.0f; - is_double = true; - } + + if (lhi->kfe == lhj->kfe) { + lhj->l = -1.0f; + is_double = true; + } + } + for (int j = i + 1; j < n; j++) { + KnifeLineHit *lhj = &linehits[j]; + if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { + break; + } + if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) { + lhj->l = -1.0f; + is_double = true; } } } @@ -2272,11 +2269,12 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, Li /* Remove dangling edges, not essential - but nice for users. */ for (i = 0; i < edge_array_len_orig; i++) { - if (kfe_array[i]) { - if (BM_edge_is_wire(kfe_array[i]->e)) { - BM_edge_kill(bm, kfe_array[i]->e); - kfe_array[i]->e = NULL; - } + if (kfe_array[i] == NULL) { + continue; + } + if (BM_edge_is_wire(kfe_array[i]->e)) { + BM_edge_kill(bm, kfe_array[i]->e); + kfe_array[i]->e = NULL; } } @@ -2588,7 +2586,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, BLI_assert(tri_i >= 0 && tri_i < tottri); for (; tri_i < tottri; tri_i++) { - float lv[3][3]; + float tri_cos[3][3]; float ray_tri_uv[2]; tri = em->looptris[tri_i]; @@ -2596,22 +2594,22 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, break; } - knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, lv); + knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, tri_cos); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with * an exact test; * We will exclude hits near real edges by a later test. */ if (isect_ray_tri_epsilon_v3( - v1, raydir, lv[0], lv[1], lv[2], &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { + v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { /* Check if line coplanar with tri. */ - normal_tri_v3(tri_norm, lv[0], lv[1], lv[2]); - plane_from_point_normal_v3(tri_plane, lv[0], tri_norm); + normal_tri_v3(tri_norm, UNPACK3(tri_cos)); + plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm); if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) && (dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) { return false; } - interp_v3_v3v3v3_uv(hit_cageco, lv[0], lv[1], lv[2], ray_tri_uv); + interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv); /* Now check that far enough away from verts and edges. */ list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { @@ -3810,7 +3808,7 @@ static void knife_reset_snap_angle_input(KnifeTool_OpData *kcd) * If scene orientation is set to anything other than global it takes priority. * Otherwise kcd->constrain_axis_mode is used. */ -static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) +static void knife_constrain_axis(KnifeTool_OpData *kcd) { /* Obtain current mouse position in world space. */ float curr_cage_adjusted[3]; @@ -3819,7 +3817,7 @@ static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) /* Constrain axes. */ Scene *scene = kcd->scene; - ViewLayer *view_layer = CTX_data_view_layer(C); + ViewLayer *view_layer = kcd->vc.view_layer; Object *obedit = (kcd->prev.ob) ? kcd->prev.ob : kcd->vc.obedit; RegionView3D *rv3d = kcd->region->regiondata; const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); @@ -3871,7 +3869,7 @@ static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) * In this case the selection-buffer is used to select the face, * then the closest `vert` or `edge` is set, and those will enable `is_co_set`. */ -static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) +static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2]) { knife_pos_data_clear(&kcd->curr); copy_v2_v2(kcd->curr.mval, mval); @@ -3894,7 +3892,7 @@ static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, cons } if (kcd->axis_constrained) { - knife_constrain_axis(C, kcd); + knife_constrain_axis(kcd); } } @@ -4075,8 +4073,7 @@ static void knife_init_colors(KnifeColors *colors) } /* called when modal loop selection gets set up... */ -static void knifetool_init(bContext *C, - ViewContext *vc, +static void knifetool_init(ViewContext *vc, KnifeTool_OpData *kcd, const bool only_select, const bool cut_through, @@ -4099,7 +4096,7 @@ static void knifetool_init(bContext *C, kcd->region = vc->region; kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - CTX_data_view_layer(C), CTX_wm_view3d(C), &kcd->objects_len); + vc->view_layer, vc->v3d, &kcd->objects_len); Object *ob; BMEditMesh *em; @@ -4241,14 +4238,14 @@ static void knifetool_exit(wmOperator *op) /** \name Mouse-Moving Event Updates * \{ */ -/* Update active knife edge/vert pointers. */ -static int knife_update_active(bContext *C, KnifeTool_OpData *kcd) +/** Update active knife edge/vert pointers. */ +static int knife_update_active(KnifeTool_OpData *kcd) { /* If no hits are found this would normally default to (0, 0, 0) so instead * get a point at the mouse ray closest to the previous point. * Note that drawing lines in `free-space` isn't properly supported * but there's no guarantee (0, 0, 0) has any geometry either - campbell */ - if (!knife_snap_update_from_mval(C, kcd, kcd->mval)) { + if (!knife_snap_update_from_mval(kcd, kcd->mval)) { float origin[3]; float origin_ofs[3]; @@ -4269,20 +4266,20 @@ static int knife_update_active(bContext *C, KnifeTool_OpData *kcd) return 1; } -static void knifetool_update_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) +static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2]) { knife_recalc_ortho(kcd); copy_v2_v2(kcd->mval, mval); - if (knife_update_active(C, kcd)) { + if (knife_update_active(kcd)) { ED_region_tag_redraw(kcd->region); } } -static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const int mval_i[2]) +static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) { const float mval[2] = {UNPACK2(mval_i)}; - knifetool_update_mval(C, kcd, mval); + knifetool_update_mval(kcd, mval); } /** \} */ @@ -4291,33 +4288,18 @@ static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const in /** \name Finalization * \{ */ -/* Called on tool confirmation. */ -static void knifetool_finish_ex(KnifeTool_OpData *kcd) +static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob) { - Object *ob; - BMEditMesh *em; - for (uint b = 0; b < kcd->objects_len; b++) { - ob = kcd->objects[b]; - em = BKE_editmesh_from_object(ob); - - knife_make_cuts(kcd, ob); - - EDBM_selectmode_flush(em); - EDBM_update(ob->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = true, - .is_destructive = true, - }); - } + knife_make_cuts(kcd, ob); } -static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint UNUSED(base_index)) +/** + * A post version is needed to to delay recalculating tessellation after making cuts. + * Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349. + */ +static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob) { - knife_make_cuts(kcd, ob); - BMEditMesh *em = BKE_editmesh_from_object(ob); - EDBM_selectmode_flush(em); EDBM_update(ob->data, &(const struct EDBMUpdate_Params){ @@ -4327,6 +4309,16 @@ static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint U }); } +/* Called on tool confirmation. */ +static void knifetool_finish_ex(KnifeTool_OpData *kcd) +{ + for (uint b = 0; b < kcd->objects_len; b++) { + Object *ob = kcd->objects[b]; + knifetool_finish_single_pre(kcd, ob); + knifetool_finish_single_post(kcd, ob); + } +} + static void knifetool_finish(wmOperator *op) { KnifeTool_OpData *kcd = op->customdata; @@ -4445,7 +4437,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { kcd->angle_snapping_increment = snapping_increment_temp; } - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); return OPERATOR_RUNNING_MODAL; @@ -4486,7 +4478,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } knifetool_undo(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); ED_region_tag_redraw(kcd->region); handled = true; break; @@ -4494,7 +4486,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->snap_midpoints = true; knife_recalc_ortho(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; @@ -4504,7 +4496,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->snap_midpoints = false; knife_recalc_ortho(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; @@ -4538,7 +4530,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) RNA_float_get(op->ptr, "angle_snapping_increment")); knifetool_disable_orientation_locking(kcd); knife_reset_snap_angle_input(kcd); - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; @@ -4623,7 +4615,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->is_drag_undo = false; /* Needed because the last face 'hit' is ignored when dragging. */ - knifetool_update_mval(C, kcd, kcd->curr.mval); + knifetool_update_mval(kcd, kcd->curr.mval); } ED_region_tag_redraw(kcd->region); @@ -4636,14 +4628,14 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) if (kcd->is_drag_hold) { kcd->is_drag_hold = false; kcd->is_drag_undo = false; - knifetool_update_mval(C, kcd, kcd->curr.mval); + knifetool_update_mval(kcd, kcd->curr.mval); } kcd->prev = kcd->curr; kcd->curr = kcd->init; knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval); - knifetool_update_mval(C, kcd, kcd->curr.mval); + knifetool_update_mval(kcd, kcd->curr.mval); knife_add_cut(kcd); @@ -4679,7 +4671,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_PASS_THROUGH; case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */ if (kcd->mode != MODE_PANNING) { - knifetool_update_mval_i(C, kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); if (kcd->is_drag_hold) { if (kcd->totlinehit >= 2) { @@ -4706,7 +4698,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { kcd->angle_snapping_increment = snapping_increment_temp; } - knife_update_active(C, kcd); + knife_update_active(kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); return OPERATOR_RUNNING_MODAL; @@ -4761,7 +4753,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) if (do_refresh) { /* We don't really need to update mval, * but this happens to be the best way to refresh at the moment. */ - knifetool_update_mval_i(C, kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); } /* Keep going until the user confirms. */ @@ -4787,8 +4779,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* alloc new customdata */ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, - &vc, + knifetool_init(&vc, kcd, only_select, cut_through, @@ -4824,7 +4815,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE); WM_event_add_modal_handler(C, op); - knifetool_update_mval_i(C, kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); if (wait_for_input == false) { /* Avoid copy-paste logic. */ @@ -4942,7 +4933,7 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) return false; } -void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) { KnifeTool_OpData *kcd; @@ -4957,8 +4948,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, - vc, + knifetool_init(vc, kcd, only_select, cut_through, @@ -4984,7 +4974,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag int i; for (i = 0; i < mval_tot; i++) { - knifetool_update_mval(C, kcd, mval_fl[i]); + knifetool_update_mval(kcd, mval_fl[i]); if (i == 0) { knife_start_cut(kcd); kcd->mode = MODE_DRAGGING; @@ -5012,7 +5002,7 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false); } - knifetool_finish_single_ex(kcd, ob, b); + knifetool_finish_single_pre(kcd, ob); /* Tag faces inside! */ if (use_tag) { @@ -5036,17 +5026,19 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag /* Tag all faces linked to cut edges. */ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { /* Check are we tagged?, then we are an original face. */ - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { - BMFace *f; - BMIter fiter; - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - mul_m4_v3(ob->obmat, cent); - knife_project_v2(kcd, cent, cent_ss); - if (edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - } + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + continue; + } + + BMFace *f; + BMIter fiter; + BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if (edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); } } } @@ -5056,43 +5048,45 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag BMFace *f; keep_search = false; BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { - /* Am I connected to a tagged face via an un-tagged edge - * (ie, not across a cut)? */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - bool found = false; - - do { - if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { - /* Now check if the adjacent faces is tagged. */ - BMLoop *l_radial_iter = l_iter->radial_next; - if (l_radial_iter != l_iter) { - do { - if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { - found = true; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && - (found == false)); - } - } - } while ((l_iter = l_iter->next) != l_first && (found == false)); - - if (found) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - mul_m4_v3(ob->obmat, cent); - knife_project_v2(kcd, cent, cent_ss); - if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && - edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - keep_search = true; - } - else { - /* Don't lose time on this face again, set it as outside. */ - F_ISECT_SET_OUTSIDE(f); + if (BM_elem_flag_test(f, BM_ELEM_TAG) || !F_ISECT_IS_UNKNOWN(f)) { + continue; + } + + /* Am I connected to a tagged face via an un-tagged edge + * (ie, not across a cut)? */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + bool found = false; + + do { + if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { + /* Now check if the adjacent faces is tagged. */ + BMLoop *l_radial_iter = l_iter->radial_next; + if (l_radial_iter != l_iter) { + do { + if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + found = true; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && + (found == false)); } } + } while ((l_iter = l_iter->next) != l_first && (found == false)); + + if (found) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && + edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + keep_search = true; + } + else { + /* Don't lose time on this face again, set it as outside. */ + F_ISECT_SET_OUTSIDE(f); + } } } } while (keep_search); @@ -5101,6 +5095,10 @@ void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag #undef F_ISECT_SET_UNKNOWN #undef F_ISECT_SET_OUTSIDE } + + /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and + * the doc-string for #knifetool_finish_single_post. */ + knifetool_finish_single_post(kcd, ob); } knifetool_exit_ex(kcd); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index bce46dd7cf7..c32b1fa99c0 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -142,7 +142,7 @@ static int knifeproject_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init_object(&vc, obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_mesh_knife(C, &vc, polys, true, cut_through); + EDBM_mesh_knife(&vc, polys, true, cut_through); /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index cafcc4ec578..feecefdb7ea 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -7032,6 +7032,14 @@ static void sort_bmelem_flag(bContext *C, } BM_mesh_remap(em->bm, map[0], map[1], map[2]); + + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = (totelem[2] != 0), + .calc_normals = false, + .is_destructive = true, + }); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index ae06cf4c3bd..67834bf05ce 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -241,7 +241,7 @@ void ED_mesh_uv_loop_reset(bContext *C, Mesh *me) WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } -int ED_mesh_uv_texture_add( +int ED_mesh_uv_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { /* NOTE: keep in sync with #ED_mesh_color_add. */ @@ -307,7 +307,7 @@ int ED_mesh_uv_texture_add( return layernum_dst; } -void ED_mesh_uv_texture_ensure(Mesh *me, const char *name) +void ED_mesh_uv_ensure(Mesh *me, const char *name) { BMEditMesh *em; int layernum_dst; @@ -317,18 +317,18 @@ void ED_mesh_uv_texture_ensure(Mesh *me, const char *name) layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, nullptr); + ED_mesh_uv_add(me, name, true, true, nullptr); } } else { layernum_dst = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, nullptr); + ED_mesh_uv_add(me, name, true, true, nullptr); } } } -bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) +bool ED_mesh_uv_remove_index(Mesh *me, const int n) { CustomData *ldata = GET_CD_DATA(me, ldata); CustomDataLayer *cdlu; @@ -348,24 +348,22 @@ bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) return true; } -bool ED_mesh_uv_texture_remove_active(Mesh *me) +bool ED_mesh_uv_remove_active(Mesh *me) { - /* texpoly/uv are assumed to be in sync */ CustomData *ldata = GET_CD_DATA(me, ldata); const int n = CustomData_get_active_layer(ldata, CD_MLOOPUV); if (n != -1) { - return ED_mesh_uv_texture_remove_index(me, n); + return ED_mesh_uv_remove_index(me, n); } return false; } -bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name) +bool ED_mesh_uv_remove_named(Mesh *me, const char *name) { - /* texpoly/uv are assumed to be in sync */ CustomData *ldata = GET_CD_DATA(me, ldata); const int n = CustomData_get_named_layer(ldata, CD_MLOOPUV, name); if (n != -1) { - return ED_mesh_uv_texture_remove_index(me, n); + return ED_mesh_uv_remove_index(me, n); } return false; } @@ -373,7 +371,7 @@ bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name) int ED_mesh_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { - /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ + /* NOTE: keep in sync with #ED_mesh_uv_add. */ BMEditMesh *em; int layernum; @@ -516,7 +514,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C) int ED_mesh_sculpt_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { - /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ + /* NOTE: keep in sync with #ED_mesh_uv_add. */ BMEditMesh *em; int layernum; @@ -650,7 +648,7 @@ static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op) Object *ob = ED_object_context(C); Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_uv_texture_add(me, nullptr, true, true, op->reports) == -1) { + if (ED_mesh_uv_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -683,7 +681,7 @@ static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op)) Object *ob = ED_object_context(C); Mesh *me = static_cast<Mesh *>(ob->data); - if (!ED_mesh_uv_texture_remove_active(me)) { + if (!ED_mesh_uv_remove_active(me)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 1ee4be50322..303234df48c 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -174,8 +174,7 @@ void MESH_OT_knife_project(struct wmOperatorType *ot); /** * \param use_tag: When set, tag all faces inside the polylines. */ -void EDBM_mesh_knife(struct bContext *C, - struct ViewContext *vc, +void EDBM_mesh_knife(struct ViewContext *vc, struct LinkNode *polys, bool use_tag, bool cut_through); diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index fafccf68f03..9e28e1bafdd 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -418,7 +418,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) } totcol = ob->totcol; - /* obact materials in new main array, is nicer start! */ + /* Active object materials in new main array, is nicer start! */ for (a = 0; a < ob->totcol; a++) { matar[a] = BKE_object_material_get(ob, a + 1); id_us_plus((ID *)matar[a]); diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index ac8ab834cb7..422aaa03120 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1265,9 +1265,9 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) "Relative Path", "Select the file relative to the blend file"); RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); - prop = RNA_def_string( - ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Image name to assign"); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + + WM_operator_properties_id_lookup(ot, true); + ED_object_add_generic_props(ot, false); } @@ -1651,19 +1651,12 @@ static std::optional<CollectionAddInfo> collection_add_info_get_from_op(bContext Main *bmain = CTX_data_main(C); PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location"); - PropertyRNA *prop_session_uuid = RNA_struct_find_property(op->ptr, "session_uuid"); - PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); + + add_info.collection = reinterpret_cast<Collection *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR)); bool update_location_if_necessary = false; - if (prop_name && RNA_property_is_set(op->ptr, prop_name)) { - char name[MAX_ID_NAME - 2]; - RNA_property_string_get(op->ptr, prop_name, name); - add_info.collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); - update_location_if_necessary = true; - } - else if (RNA_property_is_set(op->ptr, prop_session_uuid)) { - const uint32_t session_uuid = (uint32_t)RNA_property_int_get(op->ptr, prop_session_uuid); - add_info.collection = (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); + if (add_info.collection) { update_location_if_necessary = true; } else { @@ -1736,8 +1729,7 @@ static int object_instance_add_invoke(bContext *C, wmOperator *op, const wmEvent RNA_int_set(op->ptr, "drop_y", event->xy[1]); } - if (!RNA_struct_property_is_set(op->ptr, "name") && - !RNA_struct_property_is_set(op->ptr, "session_uuid")) { + if (!WM_operator_properties_id_lookup_is_set(op->ptr)) { return WM_enum_search_invoke(C, op, event); } return op->type->exec(C, op); @@ -1769,16 +1761,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) ot->prop = prop; ED_object_add_generic_props(ot, false); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the collection to add", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + WM_operator_properties_id_lookup(ot, false); object_add_drop_xy_props(ot); } @@ -1875,16 +1858,7 @@ void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the collection to add", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + WM_operator_properties_id_lookup(ot, false); ED_object_add_generic_props(ot, false); @@ -1920,18 +1894,12 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) ushort local_view_bits; float loc[3], rot[3]; - PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); PropertyRNA *prop_type = RNA_struct_find_property(op->ptr, "type"); PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location"); - /* These shouldn't fail when created by outliner dropping as it checks the ID is valid. */ - if (!RNA_property_is_set(op->ptr, prop_name) || !RNA_property_is_set(op->ptr, prop_type)) { - return OPERATOR_CANCELLED; - } const short id_type = RNA_property_enum_get(op->ptr, prop_type); - char name[MAX_ID_NAME - 2]; - RNA_property_string_get(op->ptr, prop_name, name); - id = BKE_libblock_find_name(bmain, id_type, name); + id = WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, (ID_Type)id_type); if (id == nullptr) { return OPERATOR_CANCELLED; } @@ -1974,7 +1942,7 @@ void OBJECT_OT_data_instance_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_ID_NAME - 2, "Name", "ID name to add"); + WM_operator_properties_id_lookup(ot, true); PropertyRNA *prop = RNA_def_enum(ot->srna, "type", rna_enum_id_type_items, 0, "Type", ""); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); ED_object_add_generic_props(ot, false); @@ -3802,15 +3770,13 @@ static int object_add_named_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Base *basen; - Object *ob; const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; - char name[MAX_ID_NAME - 2]; - /* find object, create fake base */ - RNA_string_get(op->ptr, "name", name); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + /* Find object, create fake base. */ + + Object *ob = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); if (ob == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object not found"); @@ -3818,7 +3784,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - basen = object_add_duplicate_internal( + Base *basen = object_add_duplicate_internal( bmain, scene, view_layer, @@ -3898,7 +3864,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot) "Linked", "Duplicate object but not object data, linking to the original data"); - RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Object name to add"); + WM_operator_properties_id_lookup(ot, true); prop = RNA_def_float_matrix( ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); @@ -3920,14 +3886,11 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob; - if (RNA_struct_property_is_set(op->ptr, "name")) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); - } - else { + Object *ob = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); + + if (!ob) { ob = OBACT(view_layer); } @@ -4006,12 +3969,25 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - RNA_def_string(ot->srna, - "name", - nullptr, - MAX_ID_NAME - 2, - "Name", - "Object name to place (when unset use the active object)"); + prop = RNA_def_string( + ot->srna, + "name", + nullptr, + MAX_ID_NAME - 2, + "Name", + "Object name to place (uses the active object when this and 'session_uuid' are unset)"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + prop = RNA_def_int(ot->srna, + "session_uuid", + 0, + INT32_MIN, + INT32_MAX, + "Session UUID", + "Session UUID of the object to place (uses the active object when this and " + "'name' are unset)", + INT32_MIN, + INT32_MAX); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); prop = RNA_def_float_matrix( ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index adac1479c7e..e5dd9fb2c8b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1243,6 +1243,9 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + /* Note: the notifier below isn't actually correct, but kept around just to be on the safe side. + * If further testing shows it's not necessary (for both bones and objects) removal is fine. */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; @@ -1312,6 +1315,9 @@ static int object_update_paths_exec(bContext *C, wmOperator *op) ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + /* Note: the notifier below isn't actually correct, but kept around just to be on the safe side. + * If further testing shows it's not necessary (for both bones and objects) removal is fine. */ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index eed0a63565e..963e92942bb 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -3399,6 +3399,7 @@ static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(o nmd->node_group = new_tree; id_us_min(&tree->id); + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a7efb45e803..abbde3b5b3a 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2267,8 +2267,12 @@ static int make_override_library_exec(bContext *C, wmOperator *op) ID *id_root = NULL; bool is_override_instancing_object = false; - GSet *user_overrides_objects_uids = BLI_gset_new( - BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable"); + + GSet *user_overrides_objects_uids = do_fully_editable ? NULL : + BLI_gset_new(BLI_ghashutil_inthash_p, + BLI_ghashutil_intcmp, + __func__); bool user_overrides_from_selected_objects = false; if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL && @@ -2316,7 +2320,10 @@ static int make_override_library_exec(bContext *C, wmOperator *op) user_overrides_from_selected_objects = true; } - if (user_overrides_from_selected_objects) { + if (do_fully_editable) { + /* Pass. */ + } + else if (user_overrides_from_selected_objects) { /* Only selected objects can be 'user overrides'. */ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) { BLI_gset_add(user_overrides_objects_uids, POINTER_FROM_UINT(ob_iter->id.session_uuid)); @@ -2336,25 +2343,34 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); ID *id_root_override; - const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, &id_root_override); - - /* Define liboverrides from selected/validated objects as user defined. */ - ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || - id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { - continue; - } - if (BLI_gset_haskey(user_overrides_objects_uids, - POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) { - id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + const bool success = BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id_root, + id_root, + &obact->id, + &id_root_override, + do_fully_editable); + + if (!do_fully_editable) { + /* Define liboverrides from selected/validated objects as user defined. */ + ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || + id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { + continue; + } + if (BLI_gset_haskey(user_overrides_objects_uids, + POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) { + id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } } - } - FOREACH_MAIN_ID_END; + FOREACH_MAIN_ID_END; - BLI_gset_free(user_overrides_objects_uids, NULL); + BLI_gset_free(user_overrides_objects_uids, NULL); + } /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ @@ -2468,6 +2484,13 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; + + prop = RNA_def_boolean(ot->srna, + "do_fully_editable", + false, + "Create Fully Editable", + "Make all created override data-blocks fully editable"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ @@ -2582,9 +2605,7 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) /** \name Drop Named Material on Object Operator * \{ */ -char *ED_object_ot_drop_named_material_tooltip(bContext *C, - PointerRNA *properties, - const int mval[2]) +char *ED_object_ot_drop_named_material_tooltip(bContext *C, const char *name, const int mval[2]) { int mat_slot = 0; Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot); @@ -2593,9 +2614,6 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, } mat_slot = max_ii(mat_slot, 1); - char name[MAX_ID_NAME - 2]; - RNA_string_get(properties, "name", name); - Material *prev_mat = BKE_object_material_get(ob, mat_slot); char *result; @@ -2617,11 +2635,9 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); mat_slot = max_ii(mat_slot, 1); - Material *ma; - char name[MAX_ID_NAME - 2]; + Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_MA); - RNA_string_get(op->ptr, "name", name); - ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); if (ob == NULL || ma == NULL) { return OPERATOR_CANCELLED; } @@ -2651,7 +2667,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - RNA_def_string(ot->srna, "name", "Material", MAX_ID_NAME - 2, "Name", "Material name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 04ae6c62aee..d7f4b71d2d0 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -179,7 +179,7 @@ struct AddOperationExecutor { curves_sculpt_ = scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_settings_ = brush_->curves_sculpt_settings; - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_re_ = brush_radius_get(*scene_, *brush_, stroke_extension); brush_pos_re_ = stroke_extension.mouse_position; use_front_face_ = brush_->flag & BRUSH_FRONTFACE; @@ -700,10 +700,9 @@ struct AddOperationExecutor { for (const NeighborInfo &neighbor : neighbors) { const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index); float neighbor_length = 0.0f; - const int tot_segments = neighbor_points.size() - 1; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1 = positions_cu[neighbor_points[segment_i]]; - const float3 &p2 = positions_cu[neighbor_points[segment_i] + 1]; + for (const int segment_i : neighbor_points.drop_back(1)) { + const float3 &p1 = positions_cu[segment_i]; + const float3 &p2 = positions_cu[segment_i + 1]; neighbor_length += math::distance(p1, p2); } length_sum += neighbor.weight * neighbor_length; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 9ebbf72806e..92ce6ba3153 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -94,11 +94,30 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu for (const int curve_i : curves_range) { const IndexRange points = curves.points_for_curve(curve_i); - const int tot_segments = points.size() - 1; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions[points[segment_i]]; - const float3 &p2_cu = positions[points[segment_i] + 1]; + if (points.size() == 1) { + const float3 &pos_cu = positions[points.first()]; + + const float depth_sq_cu = math::distance_squared(ray_start_cu, pos_cu); + if (depth_sq_cu > max_depth_sq_cu) { + continue; + } + + float2 pos_re; + ED_view3d_project_float_v2_m4(®ion, pos_cu, pos_re, projection.values); + + BrushPositionCandidate candidate; + candidate.position_cu = pos_cu; + candidate.depth_sq_cu = depth_sq_cu; + candidate.distance_sq_re = math::distance_squared(brush_pos_re, pos_re); + + update_if_better(best_candidate, candidate); + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions[segment_i]; + const float3 &p2_cu = positions[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(®ion, p1_cu, p1_re, projection.values); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index cecb13fbf7f..1fcab2290e8 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -86,7 +86,8 @@ struct CombOperationExecutor { const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; - float brush_radius_re_; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; eBrushFalloffShape falloff_shape_; @@ -126,8 +127,9 @@ struct CombOperationExecutor { curves_sculpt_ = scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); curves_to_world_mat_ = object_->obmat; world_to_curves_mat_ = curves_to_world_mat_.inverted(); @@ -212,7 +214,8 @@ struct CombOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); - const float brush_radius_sq_re = pow2f(brush_radius_re_); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Vector<int> &local_changed_curves = r_changed_curves.local(); @@ -236,7 +239,7 @@ struct CombOperationExecutor { const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re); /* A falloff that is based on how far away the point is from the stroke. */ const float radius_falloff = BKE_brush_curve_strength( - brush_, distance_to_brush_re, brush_radius_re_); + brush_, distance_to_brush_re, brush_radius_re); /* Combine the falloff and brush strength. */ const float weight = brush_strength_ * radius_falloff; @@ -280,7 +283,7 @@ struct CombOperationExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -342,7 +345,7 @@ struct CombOperationExecutor { void initialize_spherical_brush_reference_point() { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 9446f38891e..323e99df099 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -50,8 +50,6 @@ using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: - float2 brush_pos_prev_re_; - CurvesBrush3D brush_3d_; friend struct DeleteOperationExecutor; @@ -74,17 +72,16 @@ struct DeleteOperationExecutor { const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; - float brush_radius_re_; + float brush_radius_base_re_; + float brush_radius_factor_; float2 brush_pos_re_; - float2 brush_pos_prev_re_; float4x4 curves_to_world_mat_; float4x4 world_to_curves_mat_; void execute(DeleteOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { - BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; }); self_ = &self; depsgraph_ = CTX_data_depsgraph_pointer(&C); @@ -99,11 +96,10 @@ struct DeleteOperationExecutor { curves_sculpt_ = scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); brush_pos_re_ = stroke_extension.mouse_position; - brush_pos_prev_re_ = stroke_extension.is_first ? stroke_extension.mouse_position : - self.brush_pos_prev_re_; curves_to_world_mat_ = object_->obmat; world_to_curves_mat_ = curves_to_world_mat_.inverted(); @@ -159,9 +155,23 @@ struct DeleteOperationExecutor { Span<float3> positions_cu = curves_->positions(); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { for (const int curve_i : curve_range) { const IndexRange points = curves_->points_for_curve(curve_i); + if (points.size() == 1) { + const float3 pos_cu = brush_transform_inv * positions_cu[points.first()]; + float2 pos_re; + ED_view3d_project_float_v2_m4(region_, pos_cu, pos_re, projection.values); + + if (math::distance_squared(brush_pos_re_, pos_re) <= brush_radius_sq_re) { + curves_to_delete[curve_i] = true; + } + continue; + } + for (const int segment_i : points.drop_back(1)) { const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; @@ -170,8 +180,9 @@ struct DeleteOperationExecutor { ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); - const float dist = dist_seg_seg_v2(pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); - if (dist <= brush_radius_re_) { + const float dist_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, pos1_re, pos2_re); + if (dist_sq_re <= brush_radius_sq_re) { curves_to_delete[curve_i] = true; break; } @@ -185,55 +196,48 @@ struct DeleteOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); - float3 brush_start_wo, brush_end_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, - brush_pos_prev_re_, - brush_start_wo); + float3 brush_wo; ED_view3d_win_to_3d(v3d_, region_, curves_to_world_mat_ * self_->brush_3d_.position_cu, brush_pos_re_, - brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + brush_wo); + const float3 brush_cu = world_to_curves_mat_ * brush_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->delete_spherical( - brush_transform * brush_start_cu, brush_transform * brush_end_cu, curves_to_delete); + this->delete_spherical(brush_transform * brush_cu, curves_to_delete); } } - void delete_spherical(const float3 &brush_start_cu, - const float3 &brush_end_cu, - MutableSpan<bool> curves_to_delete) + void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete) { Span<float3> positions_cu = curves_->positions(); - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { for (const int curve_i : curve_range) { const IndexRange points = curves_->points_for_curve(curve_i); + + if (points.size() == 1) { + const float3 &pos_cu = positions_cu[points.first()]; + const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu); + if (distance_sq_cu < brush_radius_sq_cu) { + curves_to_delete[curve_i] = true; + } + continue; + } + for (const int segment_i : points.drop_back(1)) { const float3 &pos1_cu = positions_cu[segment_i]; const float3 &pos2_cu = positions_cu[segment_i + 1]; - float3 closest_segment_cu, closest_brush_cu; - isect_seg_seg_v3(pos1_cu, - pos2_cu, - brush_start_cu, - brush_end_cu, - closest_segment_cu, - closest_brush_cu); - const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, - closest_brush_cu); - if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu); + if (distance_sq_cu > brush_radius_sq_cu) { continue; } curves_to_delete[curve_i] = true; @@ -246,7 +250,7 @@ struct DeleteOperationExecutor { void initialize_spherical_brush_reference_point() { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index d10cf239dd2..b0a6d6ef29c 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -283,9 +283,10 @@ struct CurvesEffectOperationExecutor { CurvesGeometry *curves_ = nullptr; const Brush *brush_ = nullptr; - float brush_radius_re_; - float brush_radius_sq_re_; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; + eBrushFalloffShape falloff_shape_; float4x4 curves_to_world_mat_; @@ -321,9 +322,12 @@ struct CurvesEffectOperationExecutor { const CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt.paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); - brush_radius_sq_re_ = pow2f(brush_radius_re_); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); + + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); + falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape); curves_to_world_mat_ = object_->obmat; @@ -341,7 +345,7 @@ struct CurvesEffectOperationExecutor { *rv3d_, *object_, stroke_extension.mouse_position, - brush_radius_re_)) { + brush_radius_base_re_)) { self.brush_3d_ = *brush_3d; } } @@ -385,18 +389,20 @@ struct CurvesEffectOperationExecutor { symmetry_brush_transforms_inv.append(brush_transform.inverted()); } + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); - const int tot_segments = points.size() - 1; - float max_move_distance_cu = 0.0f; + float max_move_distance_cu = 0.0f; for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = brush_transform_inv * positions_cu[points[segment_i]]; - const float3 &p2_cu = brush_transform_inv * positions_cu[points[segment_i] + 1]; + for (const int segment_i : points.drop_back(1)) { + const float3 p1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); @@ -415,13 +421,13 @@ struct CurvesEffectOperationExecutor { p1_re, p2_re); - if (dist_to_brush_sq_re > brush_radius_sq_re_) { + if (dist_to_brush_sq_re > brush_radius_sq_re) { continue; } const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_re, brush_radius_re_); + brush_, dist_to_brush_re, brush_radius_re); const float weight = brush_strength_ * radius_falloff; const float3 closest_on_segment_cu = math::interpolate( @@ -474,7 +480,7 @@ struct CurvesEffectOperationExecutor { const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo; const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu; const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( @@ -485,15 +491,15 @@ struct CurvesEffectOperationExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); - const int tot_segments = points.size() - 1; + float max_move_distance_cu = 0.0f; for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu; const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + const float3 &p2_cu = positions_cu[segment_i + 1]; float3 closest_on_segment_cu; float3 closest_on_brush_cu; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 00e7213b5e6..842de234761 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -16,6 +16,8 @@ struct RegionView3D; struct Depsgraph; struct View3D; struct Object; +struct Brush; +struct Scene; namespace blender::ed::sculpt_paint { @@ -24,8 +26,19 @@ using bke::CurvesGeometry; struct StrokeExtension { bool is_first; float2 mouse_position; + float pressure; }; +float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension); +float brush_radius_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension); + +float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension); +float brush_strength_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension); + /** * Base class for stroke based operations in curves sculpt mode. */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 776da37205c..15d0f73592d 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -74,6 +74,36 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ +float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension) +{ + if (BKE_brush_use_size_pressure(&brush)) { + return stroke_extension.pressure; + } + return 1.0f; +} + +float brush_radius_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension) +{ + return BKE_brush_size_get(&scene, &brush) * brush_radius_factor(brush, stroke_extension); +} + +float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension) +{ + if (BKE_brush_use_alpha_pressure(&brush)) { + return stroke_extension.pressure; + } + return 1.0f; +} + +float brush_strength_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension) +{ + return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension); +} + static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, wmOperator &op) { @@ -128,6 +158,7 @@ static void stroke_update_step(bContext *C, StrokeExtension stroke_extension; RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); + stroke_extension.pressure = RNA_float_get(stroke_element, "pressure"); if (!op_data->operation) { stroke_extension.is_first = true; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index e009b443839..bcdeaaeabf2 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -80,8 +80,10 @@ struct SnakeHookOperatorExecutor { const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; - float brush_radius_re_; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; + eBrushFalloffShape falloff_shape_; Object *object_ = nullptr; @@ -112,8 +114,11 @@ struct SnakeHookOperatorExecutor { curves_sculpt_ = scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); + falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); curves_to_world_mat_ = object_->obmat; @@ -132,7 +137,7 @@ struct SnakeHookOperatorExecutor { if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -174,6 +179,9 @@ struct SnakeHookOperatorExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); @@ -183,13 +191,14 @@ struct SnakeHookOperatorExecutor { float2 old_pos_re; ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); - const float distance_to_brush_re = math::distance(old_pos_re, brush_pos_prev_re_); - if (distance_to_brush_re > brush_radius_re_) { + const float distance_to_brush_sq_re = math::distance_squared(old_pos_re, + brush_pos_prev_re_); + if (distance_to_brush_sq_re > brush_radius_sq_re) { continue; } const float radius_falloff = BKE_brush_curve_strength( - brush_, distance_to_brush_re, brush_radius_re_); + brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re); const float weight = brush_strength_ * radius_falloff; const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; @@ -222,7 +231,7 @@ struct SnakeHookOperatorExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 572e5b78b74..a313489885d 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -359,9 +359,7 @@ void paint_brush_color_get(struct Scene *scene, } /* Gradient / Color-band colors are not considered #PROP_COLOR_GAMMA. * Brush colors are expected to be in sRGB though. */ - IMB_colormanagement_scene_linear_to_srgb_v3(color_gr); - - copy_v3_v3(color, color_gr); + IMB_colormanagement_scene_linear_to_srgb_v3(color, color_gr); } else { copy_v3_v3(color, BKE_brush_color_get(scene, br)); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 16b22775b9e..fb1c8ceaa1a 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -331,8 +331,7 @@ static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary) float color[4]; const float *brush_color = secondary ? BKE_brush_secondary_color_get(scene, brush) : BKE_brush_color_get(scene, brush); - copy_v3_v3(color, brush_color); - IMB_colormanagement_srgb_to_scene_linear_v3(color); + IMB_colormanagement_srgb_to_scene_linear_v3(color, brush_color); color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */ diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 46940b619e6..644f14905ba 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -2001,7 +2001,7 @@ static void sculpt_expand_cache_initial_config_set(bContext *C, BKE_curvemapping_init(expand_cache->brush->curve); copy_v4_fl(expand_cache->fill_color, 1.0f); copy_v3_v3(expand_cache->fill_color, BKE_brush_color_get(ss->scene, expand_cache->brush)); - IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color); + IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color, expand_cache->fill_color); expand_cache->scene = CTX_data_scene(C); expand_cache->mtex = &expand_cache->brush->mtex; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index f71a814aff4..26d18823b37 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -298,7 +298,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent float fill_color[3]; RNA_float_get_array(op->ptr, "fill_color", fill_color); - IMB_colormanagement_srgb_to_scene_linear_v3(fill_color); + IMB_colormanagement_srgb_to_scene_linear_v3(fill_color, fill_color); if (filter_strength < 0.0 && !ss->filter_cache->pre_smoothed_color) { sculpt_color_presmooth_init(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 8803c95aab1..dc620f0ee93 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -781,8 +781,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent } float color_srgb[3]; - copy_v3_v3(color_srgb, active_vertex_color); - IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); + IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb, active_vertex_color); BKE_brush_color_set(scene, brush, color_srgb); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 7a8a6e8e484..ac05652b058 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -124,7 +124,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(brush_color, ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : BKE_brush_color_get(ss->scene, brush)); - IMB_colormanagement_srgb_to_scene_linear_v3(brush_color); + IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 1d0061ab7d8..052af39319c 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -724,6 +724,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) /* Needed to refresh context path when changing active particle system index. */ buttons_area_redraw(area, BCONTEXT_PARTICLE); break; + case ND_DRAW_ANIMVIZ: + buttons_area_redraw(area, BCONTEXT_OBJECT); + break; default: /* Not all object RNA props have a ND_ notifier (yet) */ ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 568bd064e3e..63f919a1713 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -103,7 +103,7 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->overlay.flag = SI_OVERLAY_SHOW_OVERLAYS | SI_OVERLAY_SHOW_GRID_BACKGROUND; BKE_imageuser_default(&simage->iuser); - simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS | IMA_SHOW_MAX_RESOLUTION; + simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS; BKE_scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 7fb15d69ab5..975d4eda7e3 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -303,10 +303,8 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, wmOperator *op, bNodeTree *ntree) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name); + bNodeTree *node_group = reinterpret_cast<bNodeTree *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT)); if (!node_group) { return nullptr; } @@ -348,8 +346,14 @@ static int node_add_group_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + const char *node_idname = node_group_idname(C); + if (node_idname[0] == '\0') { + BKE_report(op->reports, RPT_WARNING, "Could not determine type of group node"); + return OPERATOR_CANCELLED; + } + bNode *group_node = node_add_node(*C, - node_group_idname(C), + node_idname, (node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP : NODE_GROUP, snode->runtime->cursor[0], @@ -365,9 +369,25 @@ static int node_add_group_exec(bContext *C, wmOperator *op) nodeSetActive(ntree, group_node); ED_node_tree_propagate_change(C, bmain, nullptr); + DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; } +static bool node_add_group_poll(bContext *C) +{ + if (!ED_operator_node_editable(C)) { + return false; + } + const SpaceNode *snode = CTX_wm_space_node(C); + if (snode->edittree->type == NTREE_CUSTOM) { + CTX_wm_operator_poll_msg_set(C, + "This node editor displays a custom (Python defined) node tree. " + "Dropping node groups isn't supported for this."); + return false; + } + return true; +} + static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); @@ -396,12 +416,12 @@ void NODE_OT_add_group(wmOperatorType *ot) /* callbacks */ ot->exec = node_add_group_exec; ot->invoke = node_add_group_invoke; - ot->poll = ED_operator_node_editable; + ot->poll = node_add_group_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -410,26 +430,16 @@ void NODE_OT_add_group(wmOperatorType *ot) /** \name Add Node Object Operator * \{ */ -static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Object *)BKE_libblock_find_session_uuid(bmain, ID_OB, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Object *)BKE_libblock_find_name(bmain, ID_OB, name); -} - static int node_add_object_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - Object *object; - if (!(object = node_add_object_get_and_poll_object_node_tree(bmain, op))) { + Object *object = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); + + if (!object) { return OPERATOR_CANCELLED; } @@ -486,8 +496,6 @@ static bool node_add_object_poll(bContext *C) void NODE_OT_add_object(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Node Object"; ot->description = "Add an object info node to the current node editor"; @@ -501,17 +509,7 @@ void NODE_OT_add_object(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Object", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -520,27 +518,16 @@ void NODE_OT_add_object(wmOperatorType *ot) /** \name Add Node Collection Operator * \{ */ -static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *bmain, - wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); -} - static int node_add_collection_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree *ntree = snode.edittree; - Collection *collection; - if (!(collection = node_add_collection_get_and_poll_collection_node_tree(bmain, op))) { + Collection *collection = reinterpret_cast<Collection *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR)); + + if (!collection) { return OPERATOR_CANCELLED; } @@ -597,8 +584,6 @@ static bool node_add_collection_poll(bContext *C) void NODE_OT_add_collection(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Node Collection"; ot->description = "Add an collection info node to the current node editor"; @@ -612,18 +597,7 @@ void NODE_OT_add_collection(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string( - ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -745,7 +719,7 @@ void NODE_OT_add_file(wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -754,18 +728,6 @@ void NODE_OT_add_file(wmOperatorType *ot) /** \name Add Mask Node Operator * \{ */ -static ID *node_add_mask_get_and_poll_mask(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return BKE_libblock_find_session_uuid(bmain, ID_MSK, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return BKE_libblock_find_name(bmain, ID_MSK, name); -} - static bool node_add_mask_poll(bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); @@ -779,7 +741,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) SpaceNode &snode = *CTX_wm_space_node(C); bNode *node; - ID *mask = node_add_mask_get_and_poll_mask(bmain, op); + ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_MSK); if (!mask) { return OPERATOR_CANCELLED; } @@ -805,8 +767,6 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) void NODE_OT_add_mask(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Mask Node"; ot->description = "Add a mask node to the current node editor"; @@ -819,17 +779,7 @@ void NODE_OT_add_mask(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index fb2f1bf3751..ab80a44d636 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -293,10 +293,10 @@ static void compo_startjob(void *cjv, ntree->progress = nullptr; } -} // namespace blender::ed::space_node - /** \} */ +} // namespace blender::ed::space_node + /* -------------------------------------------------------------------- */ /** \name Composite Job C API * \{ */ diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index fee64da0459..9c0172cfabf 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -183,9 +183,9 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) BLI_assert(socket->type == SOCK_STRING); /* For the attribute input node, also adjust the type and links connected to the output. */ - if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) { + if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE && item->data_type.has_value()) { NodeGeometryInputNamedAttribute &storage = *(NodeGeometryInputNamedAttribute *)node->storage; - const CustomDataType new_type = data_type_in_attribute_input_node(item->data_type); + const CustomDataType new_type = data_type_in_attribute_input_node(*item->data_type); if (new_type != storage.data_type) { storage.data_type = new_type; /* Make the output socket with the new type on the attribute input node active. */ diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 3963186f73b..15afd024766 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -672,7 +672,7 @@ static void node_group_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *d { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); } static void node_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) @@ -687,7 +687,7 @@ static void node_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); RNA_struct_property_unset(drop->ptr, "filepath"); } else if (drag->path[0]) { diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 59f6bd85d59..97d2957eed2 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC outliner_draw.cc outliner_edit.cc outliner_ops.cc + outliner_query.cc outliner_select.cc outliner_sync.cc outliner_tools.cc @@ -57,6 +58,7 @@ set(SRC tree/tree_element_scene_objects.cc tree/tree_element_seq.cc tree/tree_element_view_layer.cc + tree/tree_iterator.cc outliner_intern.hh tree/common.hh @@ -75,6 +77,7 @@ set(SRC tree/tree_element_scene_objects.hh tree/tree_element_seq.hh tree/tree_element_view_layer.hh + tree/tree_iterator.hh ) set(LIB diff --git a/source/blender/editors/space_outliner/outliner_context.cc b/source/blender/editors/space_outliner/outliner_context.cc index d07b6641836..1a804cb58b8 100644 --- a/source/blender/editors/space_outliner/outliner_context.cc +++ b/source/blender/editors/space_outliner/outliner_context.cc @@ -12,23 +12,25 @@ #include "DNA_space_types.h" #include "outliner_intern.hh" +#include "tree/tree_iterator.hh" -static void outliner_context_selected_ids_recursive(const ListBase *subtree, +using namespace blender::ed::outliner; + +static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner, bContextDataResult *result) { - LISTBASE_FOREACH (const TreeElement *, te, subtree) { + tree_iterator::all(space_outliner, [&](const TreeElement *te) { const TreeStoreElem *tse = TREESTORE(te); if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))) { CTX_data_id_list_add(result, tse->id); } - outliner_context_selected_ids_recursive(&te->subtree, result); - } + }); } static void outliner_context_selected_ids(const SpaceOutliner *space_outliner, bContextDataResult *result) { - outliner_context_selected_ids_recursive(&space_outliner->tree, result); + outliner_context_selected_ids_recursive(*space_outliner, result); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index a22ce9d3d24..e20958c1b1e 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -316,7 +316,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - bool changed = outliner_flag_set(&space_outliner->tree, TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_DRAG_ANY, false); if (changed) { ED_region_tag_redraw_no_rebuild(CTX_wm_region(C)); } @@ -847,8 +847,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); - bool changed = outliner_flag_set( - &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); if (!drop_data) { @@ -1195,8 +1194,7 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); - bool changed = outliner_flag_set( - &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; if (((event->modifier & KM_SHIFT) == 0) && @@ -1461,7 +1459,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, /* Only drag element under mouse if it was not selected before. */ if ((tselem->flag & TSE_SELECTED) == 0) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); tselem->flag |= TSE_SELECTED; } diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index d165e98d7d4..753de83a10d 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -71,7 +71,9 @@ #include "tree/tree_element_id.hh" #include "tree/tree_element_overrides.hh" #include "tree/tree_element_rna.hh" +#include "tree/tree_iterator.hh" +using namespace blender; using namespace blender::ed::outliner; /* -------------------------------------------------------------------- */ @@ -1714,72 +1716,70 @@ static void outliner_draw_restrictbuts(uiBlock *block, } static void outliner_draw_userbuts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb) + const ARegion *region, + const SpaceOutliner *space_outliner) { + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + return; + } - LISTBASE_FOREACH (TreeElement *, te, lb) { - TreeStoreElem *tselem = TREESTORE(te); - if (outliner_is_element_in_view(te, ®ion->v2d)) { - if (tselem->type == TSE_SOME_ID) { - uiBut *bt; - ID *id = tselem->id; - const char *tip = nullptr; - char buf[16] = ""; - int but_flag = UI_BUT_DRAG_LOCK; + const TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type != TSE_SOME_ID) { + return; + } - if (ID_IS_LINKED(id)) { - but_flag |= UI_BUT_DISABLED; - } + uiBut *bt; + ID *id = tselem->id; + const char *tip = nullptr; + char buf[16] = ""; + int but_flag = UI_BUT_DRAG_LOCK; - BLI_str_format_int_grouped(buf, id->us); - bt = uiDefBut(block, - UI_BTYPE_BUT, - 1, - buf, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0, - 0, - TIP_("Number of users of this data-block")); - UI_but_flag_enable(bt, but_flag); - - if (id->flag & LIB_FAKEUSER) { - tip = TIP_("Data-block will be retained using a fake user"); - } - else { - tip = TIP_("Data-block has no users and will be deleted"); - } - bt = uiDefIconButBitS(block, - UI_BTYPE_ICON_TOGGLE, - LIB_FAKEUSER, - 1, - ICON_FAKE_USER_OFF, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - &id->flag, - 0, - 0, - 0, - 0, - tip); - UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr); - UI_but_flag_enable(bt, but_flag); - } + if (ID_IS_LINKED(id)) { + but_flag |= UI_BUT_DISABLED; } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_userbuts(block, region, space_outliner, &te->subtree); + BLI_str_format_int_grouped(buf, id->us); + bt = uiDefBut(block, + UI_BTYPE_BUT, + 1, + buf, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0, + 0, + TIP_("Number of users of this data-block")); + UI_but_flag_enable(bt, but_flag); + + if (id->flag & LIB_FAKEUSER) { + tip = TIP_("Data-block will be retained using a fake user"); } - } + else { + tip = TIP_("Data-block has no users and will be deleted"); + } + bt = uiDefIconButBitS(block, + UI_BTYPE_ICON_TOGGLE, + LIB_FAKEUSER, + 1, + ICON_FAKE_USER_OFF, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + &id->flag, + 0, + 0, + 0, + 0, + tip); + UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr); + UI_but_flag_enable(bt, but_flag); + }); } static void outliner_draw_overrides_rna_buts(uiBlock *block, @@ -1920,91 +1920,6 @@ static void outliner_draw_overrides_restrictbuts(Main *bmain, } } -static bool outliner_draw_overrides_warning_buts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb, - const bool is_open) -{ - bool any_item_has_warnings = false; - - LISTBASE_FOREACH (TreeElement *, te, lb) { - bool item_has_warnings = false; - const bool do_draw = outliner_is_element_in_view(te, ®ion->v2d); - int but_flag = UI_BUT_DRAG_LOCK; - const char *tip = nullptr; - - TreeStoreElem *tselem = TREESTORE(te); - switch (tselem->type) { - case TSE_LIBRARY_OVERRIDE_BASE: { - ID *id = tselem->id; - - if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_( - "This override data-block is not needed anymore, but was detected as user-edited"); - } - } - else if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_("This override data-block is unused"); - } - } - break; - } - case TSE_LIBRARY_OVERRIDE: { - TreeElementOverridesProperty &te_override_prop = - *tree_element_cast<TreeElementOverridesProperty>(te); - if (!te_override_prop.is_rna_path_valid) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_( - "This override property does not exist in current data, it will be removed on " - "next .blend file save"); - } - } - break; - } - default: - break; - } - - const bool any_child_has_warnings = outliner_draw_overrides_warning_buts( - block, - region, - space_outliner, - &te->subtree, - is_open && TSELEM_OPEN(tselem, space_outliner)); - - if (do_draw && - (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) { - if (tip == nullptr) { - tip = TIP_("Some sub-items require attention"); - } - uiBut *bt = uiDefIconBut(block, - UI_BTYPE_BUT, - 1, - ICON_ERROR, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - UI_but_flag_enable(bt, but_flag); - } - any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings; - } - - return any_item_has_warnings; -} - static void outliner_draw_separator(ARegion *region, const int x) { View2D *v2d = ®ion->v2d; @@ -2025,81 +1940,82 @@ static void outliner_draw_separator(ARegion *region, const int x) immUnbindProgram(); } -static void outliner_draw_rnabuts( - uiBlock *block, ARegion *region, SpaceOutliner *space_outliner, int sizex, ListBase *lb) +static void outliner_draw_rnabuts(uiBlock *block, + ARegion *region, + SpaceOutliner *space_outliner, + int sizex) { PointerRNA ptr; PropertyRNA *prop; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - if (outliner_is_element_in_view(te, ®ion->v2d)) { - if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { - ptr = te_rna_prop->getPointerRNA(); - prop = te_rna_prop->getPropertyRNA(); - - if (!TSELEM_OPEN(tselem, space_outliner)) { - if (RNA_property_type(prop) == PROP_POINTER) { - uiBut *but = uiDefAutoButR(block, - &ptr, - prop, - -1, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - else if (RNA_property_type(prop) == PROP_ENUM) { - uiDefAutoButR(block, - &ptr, - prop, - -1, - nullptr, - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - else { - uiDefAutoButR(block, - &ptr, - prop, - -1, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - } - } - else if (TreeElementRNAArrayElement *te_rna_array_elem = - tree_element_cast<TreeElementRNAArrayElement>(te)) { - ptr = te_rna_array_elem->getPointerRNA(); - prop = te_rna_array_elem->getPropertyRNA(); - - uiDefAutoButR(block, - &ptr, - prop, - te->index, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_rnabuts(block, region, space_outliner, sizex, &te->subtree); - } - } + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + return; + } + + if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { + ptr = te_rna_prop->getPointerRNA(); + prop = te_rna_prop->getPropertyRNA(); + + if (!TSELEM_OPEN(tselem, space_outliner)) { + if (RNA_property_type(prop) == PROP_POINTER) { + uiBut *but = uiDefAutoButR(block, + &ptr, + prop, + -1, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else if (RNA_property_type(prop) == PROP_ENUM) { + uiDefAutoButR(block, + &ptr, + prop, + -1, + nullptr, + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + else { + uiDefAutoButR(block, + &ptr, + prop, + -1, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + } + } + else if (TreeElementRNAArrayElement *te_rna_array_elem = + tree_element_cast<TreeElementRNAArrayElement>(te)) { + ptr = te_rna_array_elem->getPointerRNA(); + prop = te_rna_array_elem->getPropertyRNA(); + + uiDefAutoButR(block, + &ptr, + prop, + te->index, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + }); } static void outliner_buttons(const bContext *C, @@ -2185,9 +2101,9 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED static void outliner_draw_mode_column_toggle(uiBlock *block, TreeViewContext *tvc, TreeElement *te, - TreeStoreElem *tselem, const bool lock_object_modes) { + TreeStoreElem *tselem = TREESTORE(te); if ((tselem->type != TSE_SOME_ID) || (te->idcode != ID_OB)) { return; } @@ -2258,59 +2174,63 @@ static void outliner_draw_mode_column_toggle(uiBlock *block, } } -static void outliner_draw_mode_column(const bContext *C, - uiBlock *block, +static void outliner_draw_mode_column(uiBlock *block, TreeViewContext *tvc, - SpaceOutliner *space_outliner, - ListBase *tree) + SpaceOutliner *space_outliner) { - TreeStoreElem *tselem; const bool lock_object_modes = tvc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK; - LISTBASE_FOREACH (TreeElement *, te, tree) { - tselem = TREESTORE(te); - + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { if (tvc->obact && tvc->obact->mode != OB_MODE_OBJECT) { - outliner_draw_mode_column_toggle(block, tvc, te, tselem, lock_object_modes); + outliner_draw_mode_column_toggle(block, tvc, te, lock_object_modes); } + }); +} - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_mode_column(C, block, tvc, space_outliner, &te->subtree); +static StringRefNull outliner_draw_get_warning_tree_element_subtree(const TreeElement *parent_te) +{ + LISTBASE_FOREACH (const TreeElement *, sub_te, &parent_te->subtree) { + const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(sub_te); + StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : ""; + + if (!warning_msg.is_empty()) { + return warning_msg; + } + + warning_msg = outliner_draw_get_warning_tree_element_subtree(sub_te); + if (!warning_msg.is_empty()) { + return warning_msg; } } + + return ""; } -/* Returns `true` if some warning was drawn for that element or one of its sub-elements (if it is - * not open). */ -static bool outliner_draw_warning_tree_element(uiBlock *block, - SpaceOutliner *space_outliner, - TreeElement *te, - TreeStoreElem *tselem, - const bool use_mode_column, - const int te_ys) +static StringRefNull outliner_draw_get_warning_tree_element(const SpaceOutliner &space_outliner, + const TreeElement *te) { - if ((te->flag & TE_HAS_WARNING) == 0) { - /* If given element has no warning, recursively try to display the first sub-elements' warning. - */ - if (!TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, sub_te, &te->subtree) { - TreeStoreElem *sub_tselem = TREESTORE(sub_te); + const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(te); + const StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : ""; - if (outliner_draw_warning_tree_element( - block, space_outliner, sub_te, sub_tselem, use_mode_column, te_ys)) { - return true; - } - } - } - return false; + if (!warning_msg.is_empty()) { + return warning_msg; + } + + /* If given element has no warning, recursively try to display the first sub-element's warning. + */ + if (!TSELEM_OPEN(te->store_elem, &space_outliner)) { + return outliner_draw_get_warning_tree_element_subtree(te); } - int icon = ICON_NONE; - const char *tip = ""; - const bool has_warning = tree_element_warnings_get(te, &icon, &tip); - BLI_assert(has_warning); - UNUSED_VARS_NDEBUG(has_warning); + return ""; +} +static void outliner_draw_warning_tree_element(uiBlock *block, + const SpaceOutliner *space_outliner, + StringRefNull warning_msg, + const bool use_mode_column, + const int te_ys) +{ /* Move the warnings a unit left in view layer mode. */ const short mode_column_offset = (use_mode_column && (space_outliner->outlinevis == SO_SCENES)) ? UI_UNIT_X : @@ -2320,7 +2240,7 @@ static bool outliner_draw_warning_tree_element(uiBlock *block, uiBut *but = uiDefIconBut(block, UI_BTYPE_ICON_TOGGLE, 0, - icon, + ICON_ERROR, mode_column_offset, te_ys, UI_UNIT_X, @@ -2330,28 +2250,25 @@ static bool outliner_draw_warning_tree_element(uiBlock *block, 0.0, 0.0, 0.0, - tip); + warning_msg.c_str()); /* No need for undo here, this is a pure info widget. */ UI_but_flag_disable(but, UI_BUT_UNDO); - - return true; } -static void outliner_draw_warning_column(const bContext *C, - uiBlock *block, - SpaceOutliner *space_outliner, - const bool use_mode_column, - ListBase *tree) +static void outliner_draw_warning_column(uiBlock *block, + const SpaceOutliner *space_outliner, + const bool use_mode_column) { - LISTBASE_FOREACH (TreeElement *, te, tree) { - TreeStoreElem *tselem = TREESTORE(te); + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { + /* Get warning for this element, or if there is none and the element is collapsed, the first + * warning in the collapsed sub-tree. */ + StringRefNull warning_msg = outliner_draw_get_warning_tree_element(*space_outliner, te); - outliner_draw_warning_tree_element(block, space_outliner, te, tselem, use_mode_column, te->ys); - - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &te->subtree); + if (!warning_msg.is_empty()) { + outliner_draw_warning_tree_element( + block, space_outliner, warning_msg, use_mode_column, te->ys); } - } + }); } /** \} */ @@ -3232,18 +3149,16 @@ static void outliner_draw_iconrow(bContext *C, } /* closed tree element */ -static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty) +static void outliner_set_subtree_coords(const TreeElement *te) { - /* closed items may be displayed in row of parent, don't change their coordinate! */ - if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { - te->xs = 0; - te->ys = 0; - te->xend = 0; - } - - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty); - } + tree_iterator::all(te->subtree, [&](TreeElement *te) { + /* closed items may be displayed in row of parent, don't change their coordinate! */ + if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { + te->xs = 0; + te->ys = 0; + te->xend = 0; + } + }); } static bool element_should_draw_faded(const TreeViewContext *tvc, @@ -3495,10 +3410,7 @@ static void outliner_draw_tree_element(bContext *C, } } else { - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coord_tree_element(ten, startx, *starty); - } - + outliner_set_subtree_coords(te); *starty -= UI_UNIT_Y; } } @@ -3654,22 +3566,21 @@ static void outliner_draw_struct_marks(ARegion *region, } } -static void outliner_draw_highlights_recursive(uint pos, - const ARegion *region, - const SpaceOutliner *space_outliner, - const ListBase *lb, - const float col_selection[4], - const float col_active[4], - const float col_highlight[4], - const float col_searchmatch[4], - int start_x, - int *io_start_y) +static void outliner_draw_highlights(uint pos, + const ARegion *region, + const SpaceOutliner *space_outliner, + const float col_selection[4], + const float col_active[4], + const float col_highlight[4], + const float col_searchmatch[4], + int start_x, + int *io_start_y) { const bool is_searching = (SEARCHING_OUTLINER(space_outliner) || (space_outliner->outlinevis == SO_DATA_API && space_outliner->search_string[0] != 0)); - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { const TreeStoreElem *tselem = TREESTORE(te); const int start_y = *io_start_y; @@ -3725,19 +3636,7 @@ static void outliner_draw_highlights_recursive(uint pos, } *io_start_y -= UI_UNIT_Y; - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_highlights_recursive(pos, - region, - space_outliner, - &te->subtree, - col_selection, - col_active, - col_highlight, - col_searchmatch, - start_x + UI_UNIT_X, - io_start_y); - } - } + }); } static void outliner_draw_highlights(ARegion *region, @@ -3759,16 +3658,15 @@ static void outliner_draw_highlights(ARegion *region, GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - outliner_draw_highlights_recursive(pos, - region, - space_outliner, - &space_outliner->tree, - col_selection, - col_active, - col_highlight, - col_searchmatch, - startx, - starty); + outliner_draw_highlights(pos, + region, + space_outliner, + col_selection, + col_active, + col_highlight, + col_searchmatch, + startx, + starty); immUnbindProgram(); GPU_blend(GPU_BLEND_NONE); } @@ -3961,13 +3859,8 @@ void draw_outliner(const bContext *C) UI_view2d_view_ortho(v2d); /* Only show mode column in View Layers and Scenes view. */ - const bool use_mode_column = (space_outliner->flag & SO_MODE_COLUMN) && - (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)); - - const bool use_warning_column = ELEM(space_outliner->outlinevis, - SO_LIBRARIES, - SO_OVERRIDES_LIBRARY) && - space_outliner->runtime->tree_display->hasWarnings(); + const bool use_mode_column = outliner_shows_mode_column(*space_outliner); + const bool use_warning_column = outliner_has_element_warnings(*space_outliner); /* Draw outliner stuff (background, hierarchy lines and names). */ const float right_column_width = outliner_right_columns_width(space_outliner); @@ -3997,18 +3890,14 @@ void draw_outliner(const bContext *C) outliner_draw_separator(region, buttons_start_x + OL_RNA_COL_SIZEX); UI_block_emboss_set(block, UI_EMBOSS); - outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x, &space_outliner->tree); + outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x); UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); } else if (space_outliner->outlinevis == SO_ID_ORPHANS) { /* draw user toggle columns */ - outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree); + outliner_draw_userbuts(block, region, space_outliner); } else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) { - /* Draw overrides status columns. */ - outliner_draw_overrides_warning_buts( - block, region, space_outliner, &space_outliner->tree, true); - const int x = region->v2d.cur.xmax - right_column_width; outliner_draw_separator(region, x); if (space_outliner->lib_override_view_mode == SO_LIB_OVERRIDE_VIEW_PROPERTIES) { @@ -4037,12 +3926,12 @@ void draw_outliner(const bContext *C) /* Draw mode icons */ if (use_mode_column) { - outliner_draw_mode_column(C, block, &tvc, space_outliner, &space_outliner->tree); + outliner_draw_mode_column(block, &tvc, space_outliner); } /* Draw warning icons */ if (use_warning_column) { - outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &space_outliner->tree); + outliner_draw_warning_column(block, space_outliner, use_mode_column); } UI_block_emboss_set(block, UI_EMBOSS); diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 1de45b0ec96..c4a9398a5f7 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -60,6 +60,7 @@ #include "outliner_intern.hh" #include "tree/tree_element_rna.hh" +#include "tree/tree_iterator.hh" using namespace blender::ed::outliner; @@ -107,7 +108,7 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const if (!hovered_te || !is_over_icon || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED) || !(icon_te->store_elem->flag & TSE_HIGHLIGHTED_ICON)) { /* Clear highlights when nothing is hovered or when a new item is hovered. */ - changed = outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); if (hovered_te) { hovered_te->store_elem->flag |= TSE_HIGHLIGHTED; changed = true; @@ -167,7 +168,7 @@ void outliner_item_openclose(SpaceOutliner *space_outliner, } if (toggle_all) { - outliner_flag_set(&te->subtree, TSE_CLOSED, !open); + outliner_flag_set(te->subtree, TSE_CLOSED, !open); } } @@ -1077,11 +1078,16 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel) return 0; } -bool outliner_flag_set(ListBase *lb, short flag, short set) +bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set) +{ + return outliner_flag_set(space_outliner.tree, flag, set); +} + +bool outliner_flag_set(const ListBase &lb, const short flag, const short set) { bool changed = false; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all(lb, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); bool has_flag = (tselem->flag & flag); if (set == 0) { @@ -1094,21 +1100,24 @@ bool outliner_flag_set(ListBase *lb, short flag, short set) tselem->flag |= flag; changed = true; } - changed |= outliner_flag_set(&te->subtree, flag, set); - } + }); return changed; } -bool outliner_flag_flip(ListBase *lb, short flag) +bool outliner_flag_flip(const SpaceOutliner &space_outliner, const short flag) +{ + return outliner_flag_flip(space_outliner.tree, flag); +} + +bool outliner_flag_flip(const ListBase &lb, const short flag) { bool changed = false; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all(lb, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); tselem->flag ^= flag; - changed |= outliner_flag_flip(&te->subtree, flag); - } + }); return changed; } @@ -1125,10 +1134,10 @@ static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op)) ARegion *region = CTX_wm_region(C); if (outliner_flag_is_any_test(&space_outliner->tree, TSE_CLOSED, 1)) { - outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 0); + outliner_flag_set(*space_outliner, TSE_CLOSED, 0); } else { - outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 1); + outliner_flag_set(*space_outliner, TSE_CLOSED, 1); } ED_region_tag_redraw(region); @@ -1169,13 +1178,13 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op) switch (action) { case SEL_SELECT: - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 1); + outliner_flag_set(*space_outliner, TSE_SELECTED, 1); break; case SEL_DESELECT: - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); break; case SEL_INVERT: - outliner_flag_flip(&space_outliner->tree, TSE_SELECTED); + outliner_flag_flip(*space_outliner, TSE_SELECTED); break; } @@ -1211,32 +1220,16 @@ void OUTLINER_OT_select_all(wmOperatorType *ot) /** \name View Show Active (Outliner) Operator * \{ */ -static void outliner_set_coordinates_element_recursive(SpaceOutliner *space_outliner, - TreeElement *te, - int startx, - int *starty) -{ - TreeStoreElem *tselem = TREESTORE(te); - - /* store coord and continue, we need coordinates for elements outside view too */ - te->xs = (float)startx; - te->ys = (float)(*starty); - *starty -= UI_UNIT_Y; - - if (TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coordinates_element_recursive(space_outliner, ten, startx + UI_UNIT_X, starty); - } - } -} - -void outliner_set_coordinates(ARegion *region, SpaceOutliner *space_outliner) +void outliner_set_coordinates(const ARegion *region, const SpaceOutliner *space_outliner) { int starty = (int)(region->v2d.tot.ymax) - UI_UNIT_Y; - LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - outliner_set_coordinates_element_recursive(space_outliner, te, 0, &starty); - } + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { + /* store coord and continue, we need coordinates for elements outside view too */ + te->xs = 0; + te->ys = (float)starty; + starty -= UI_UNIT_Y; + }); } /* return 1 when levels were opened */ @@ -1624,11 +1617,11 @@ static int subtree_has_objects(ListBase *lb) return 0; } -/* recursive helper function for Show Hierarchy operator */ -static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner, ListBase *lb) +/* Helper function for Show Hierarchy operator */ +static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner) { /* open all object elems, close others */ - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (ELEM(tselem->type, @@ -1656,11 +1649,7 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outli else { tselem->flag |= TSE_CLOSED; } - - if (TSELEM_OPEN(tselem, space_outliner)) { - tree_element_show_hierarchy(scene, space_outliner, &te->subtree); - } - } + }); } /* show entire object level hierarchy */ @@ -1671,7 +1660,7 @@ static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); /* recursively open/close levels */ - tree_element_show_hierarchy(scene, space_outliner, &space_outliner->tree); + tree_element_show_hierarchy(scene, space_outliner); ED_region_tag_redraw(region); @@ -1873,79 +1862,75 @@ enum { DRIVERS_EDITMODE_REMOVE, } /*eDrivers_EditModes*/; -/* Recursively iterate over tree, finding and working on selected items */ +/* Iterate over tree, finding and working on selected items */ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner, - ListBase *tree, ReportList *reports, short mode) { - LISTBASE_FOREACH (TreeElement *, te, tree) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); /* if item is selected, perform operation */ - if (tselem->flag & TSE_SELECTED) { - ID *id = nullptr; - char *path = nullptr; - int array_index = 0; - short flag = 0; - short groupmode = KSP_GROUP_KSNAME; - - TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); - PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; - PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; - - /* check if RNA-property described by this selected element is an animatable prop */ - if (prop && RNA_property_animateable(&ptr, prop)) { - /* get id + path + index info from the selected element */ - tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); - } + if (!(tselem->flag & TSE_SELECTED)) { + return; + } - /* only if ID and path were set, should we perform any actions */ - if (id && path) { - short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; - int arraylen = 1; + ID *id = nullptr; + char *path = nullptr; + int array_index = 0; + short flag = 0; + short groupmode = KSP_GROUP_KSNAME; - /* array checks */ - if (flag & KSP_FLAG_WHOLE_ARRAY) { - /* entire array was selected, so add drivers for all */ - arraylen = RNA_property_array_length(&ptr, prop); - } - else { - arraylen = array_index; - } + TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; + PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; - /* we should do at least one step */ - if (arraylen == array_index) { - arraylen++; - } + /* check if RNA-property described by this selected element is an animatable prop */ + if (prop && RNA_property_animateable(&ptr, prop)) { + /* get id + path + index info from the selected element */ + tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); + } - /* for each array element we should affect, add driver */ - for (; array_index < arraylen; array_index++) { - /* action depends on mode */ - switch (mode) { - case DRIVERS_EDITMODE_ADD: { - /* add a new driver with the information obtained (only if valid) */ - ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); - break; - } - case DRIVERS_EDITMODE_REMOVE: { - /* remove driver matching the information obtained (only if valid) */ - ANIM_remove_driver(reports, id, path, array_index, dflags); - break; - } + /* only if ID and path were set, should we perform any actions */ + if (id && path) { + short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; + int arraylen = 1; + + /* array checks */ + if (flag & KSP_FLAG_WHOLE_ARRAY) { + /* entire array was selected, so add drivers for all */ + arraylen = RNA_property_array_length(&ptr, prop); + } + else { + arraylen = array_index; + } + + /* we should do at least one step */ + if (arraylen == array_index) { + arraylen++; + } + + /* for each array element we should affect, add driver */ + for (; array_index < arraylen; array_index++) { + /* action depends on mode */ + switch (mode) { + case DRIVERS_EDITMODE_ADD: { + /* add a new driver with the information obtained (only if valid) */ + ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); + break; + } + case DRIVERS_EDITMODE_REMOVE: { + /* remove driver matching the information obtained (only if valid) */ + ANIM_remove_driver(reports, id, path, array_index, dflags); + break; } } - - /* free path, since it had to be generated */ - MEM_freeN(path); } - } - /* go over sub-tree */ - if (TSELEM_OPEN(tselem, space_outliner)) { - do_outliner_drivers_editop(space_outliner, &te->subtree, reports, mode); + /* free path, since it had to be generated */ + MEM_freeN(path); } - } + }); } /** \} */ @@ -1964,8 +1949,7 @@ static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_drivers_editop( - space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_ADD); + do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_ADD); /* send notifiers */ WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, nullptr); /* XXX */ @@ -2004,8 +1988,7 @@ static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_drivers_editop( - space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE); + do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_REMOVE); /* send notifiers */ WM_event_add_notifier(C, ND_KEYS, nullptr); /* XXX */ @@ -2072,68 +2055,64 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add) return ks; } -/* Recursively iterate over tree, finding and working on selected items */ +/* Iterate over tree, finding and working on selected items */ static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner, KeyingSet *ks, - ListBase *tree, - short mode) + const short mode) { - LISTBASE_FOREACH (TreeElement *, te, tree) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); /* if item is selected, perform operation */ - if (tselem->flag & TSE_SELECTED) { - ID *id = nullptr; - char *path = nullptr; - int array_index = 0; - short flag = 0; - short groupmode = KSP_GROUP_KSNAME; - - /* check if RNA-property described by this selected element is an animatable prop */ - const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); - PointerRNA ptr = te_rna->getPointerRNA(); - if (te_rna && te_rna->getPropertyRNA() && - RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { - /* get id + path + index info from the selected element */ - tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); - } + if (!(tselem->flag & TSE_SELECTED)) { + return; + } - /* only if ID and path were set, should we perform any actions */ - if (id && path) { - /* action depends on mode */ - switch (mode) { - case KEYINGSET_EDITMODE_ADD: { - /* add a new path with the information obtained (only if valid) */ - /* TODO: what do we do with group name? - * for now, we don't supply one, and just let this use the KeyingSet name */ - BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode); - ks->active_path = BLI_listbase_count(&ks->paths); - break; - } - case KEYINGSET_EDITMODE_REMOVE: { - /* find the relevant path, then remove it from the KeyingSet */ - KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode); + ID *id = nullptr; + char *path = nullptr; + int array_index = 0; + short flag = 0; + short groupmode = KSP_GROUP_KSNAME; + + /* check if RNA-property described by this selected element is an animatable prop */ + const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna->getPointerRNA(); + if (te_rna && te_rna->getPropertyRNA() && + RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { + /* get id + path + index info from the selected element */ + tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); + } - if (ksp) { - /* free path's data */ - BKE_keyingset_free_path(ks, ksp); + /* only if ID and path were set, should we perform any actions */ + if (id && path) { + /* action depends on mode */ + switch (mode) { + case KEYINGSET_EDITMODE_ADD: { + /* add a new path with the information obtained (only if valid) */ + /* TODO: what do we do with group name? + * for now, we don't supply one, and just let this use the KeyingSet name */ + BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode); + ks->active_path = BLI_listbase_count(&ks->paths); + break; + } + case KEYINGSET_EDITMODE_REMOVE: { + /* find the relevant path, then remove it from the KeyingSet */ + KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode); - ks->active_path = 0; - } - break; + if (ksp) { + /* free path's data */ + BKE_keyingset_free_path(ks, ksp); + + ks->active_path = 0; } + break; } - - /* free path, since it had to be generated */ - MEM_freeN(path); } - } - /* go over sub-tree */ - if (TSELEM_OPEN(tselem, space_outliner)) { - do_outliner_keyingset_editop(space_outliner, ks, &te->subtree, mode); + /* free path, since it had to be generated */ + MEM_freeN(path); } - } + }); } /** \} */ @@ -2158,7 +2137,7 @@ static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_keyingset_editop(space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_ADD); + do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_ADD); /* send notifiers */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr); @@ -2199,8 +2178,7 @@ static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(o } /* recursively go into tree, adding selected items */ - do_outliner_keyingset_editop( - space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_REMOVE); + do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_REMOVE); /* send notifiers */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr); diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index f3bcb7b0f1e..a0dcb49aa43 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -160,8 +160,6 @@ enum { /* Child elements of the same type in the icon-row are drawn merged as one icon. * This flag is set for an element that is part of these merged child icons. */ TE_ICONROW_MERGED = (1 << 7), - /* This element has some warning to be displayed. */ - TE_HAS_WARNING = (1 << 8), }; /* button events */ @@ -410,8 +408,12 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, int curlevel); * Set or unset \a flag for all outliner elements in \a lb and sub-trees. * \return if any flag was modified. */ -bool outliner_flag_set(ListBase *lb, short flag, short set); -bool outliner_flag_flip(ListBase *lb, short flag); +extern "C++" { +bool outliner_flag_set(const SpaceOutliner &space_outliner, short flag, short set); +bool outliner_flag_set(const ListBase &lb, short flag, short set); +bool outliner_flag_flip(const SpaceOutliner &space_outliner, short flag); +bool outliner_flag_flip(const ListBase &lb, short flag); +} void item_rename_fn(struct bContext *C, struct ReportList *reports, @@ -453,7 +455,8 @@ void id_remap_fn(struct bContext *C, /** * To retrieve coordinates with redrawing the entire tree. */ -void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner); +void outliner_set_coordinates(const struct ARegion *region, + const struct SpaceOutliner *space_outliner); /** * Open or close a tree element, optionally toggling all children recursively. @@ -510,6 +513,11 @@ void OUTLINER_OT_drivers_delete_selected(struct wmOperatorType *ot); void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); +/* outliner_query.cc ---------------------------------------------- */ + +bool outliner_shows_mode_column(const SpaceOutliner &space_outliner); +bool outliner_has_element_warnings(const SpaceOutliner &space_outliner); + /* outliner_tools.c ---------------------------------------------- */ void merged_element_search_menu_invoke(struct bContext *C, diff --git a/source/blender/editors/space_outliner/outliner_query.cc b/source/blender/editors/space_outliner/outliner_query.cc new file mode 100644 index 00000000000..d6483c44fce --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_query.cc @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include <functional> + +#include "BLI_listbase.h" + +#include "DNA_space_types.h" + +#include "outliner_intern.hh" +#include "tree/tree_display.hh" + +using namespace blender::ed::outliner; + +bool outliner_shows_mode_column(const SpaceOutliner &space_outliner) +{ + const AbstractTreeDisplay &tree_display = *space_outliner.runtime->tree_display; + + return tree_display.supportsModeColumn() && (space_outliner.flag & SO_MODE_COLUMN); +} + +/** + * Iterate over the entire tree (including collapsed sub-elements), probing if any of the elements + * has a warning to be displayed. + */ +bool outliner_has_element_warnings(const SpaceOutliner &space_outliner) +{ + std::function<bool(const ListBase &)> recursive_fn; + + recursive_fn = [&](const ListBase &lb) { + LISTBASE_FOREACH (const TreeElement *, te, &lb) { + if (te->abstract_element && !te->abstract_element->getWarning().is_empty()) { + return true; + } + + if (recursive_fn(te->subtree)) { + return true; + } + } + + return false; + }; + + return recursive_fn(space_outliner.tree); +} diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index fd0ee422df0..bd6d3d89706 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -66,7 +66,9 @@ #include "RNA_prototypes.h" #include "outliner_intern.hh" +#include "tree/tree_display.hh" #include "tree/tree_element_seq.hh" +#include "tree/tree_iterator.hh" using namespace blender::ed::outliner; @@ -1456,7 +1458,7 @@ void outliner_item_select(bContext *C, /* Clear previous active when activating and clear selection when not extending selection */ const short clear_flag = (activate ? TSE_ACTIVE : 0) | (extend ? 0 : TSE_SELECTED); if (clear_flag) { - outliner_flag_set(&space_outliner->tree, clear_flag, false); + outliner_flag_set(*space_outliner, clear_flag, false); } if (select_flag & OL_ITEM_SELECT) { @@ -1530,7 +1532,7 @@ static void do_outliner_range_select(bContext *C, const bool active_selected = (tselem->flag & TSE_SELECTED); if (!extend) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); + outliner_flag_set(*space_outliner, TSE_SELECTED, false); } /* Select active if under cursor */ @@ -1557,12 +1559,11 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_ou bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2]) { - /* Mode toggles only show in View Layer and Scenes modes. */ - if (!ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) { + if (!outliner_shows_mode_column(*space_outliner)) { return false; } - return space_outliner->flag & SO_MODE_COLUMN && view_mval[0] < UI_UNIT_X; + return view_mval[0] < UI_UNIT_X; } static bool outliner_is_co_within_active_mode_column(bContext *C, @@ -1604,7 +1605,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C, if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) { if (deselect_all) { - changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); + changed |= outliner_flag_set(*space_outliner, TSE_SELECTED, false); } } /* Don't allow toggle on scene collection */ @@ -1715,26 +1716,17 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) /** \name Box Select Operator * \{ */ -static void outliner_item_box_select(bContext *C, - SpaceOutliner *space_outliner, - Scene *scene, - rctf *rectf, - TreeElement *te, - bool select) +static void outliner_box_select(bContext *C, + SpaceOutliner *space_outliner, + const rctf *rectf, + const bool select) { - TreeStoreElem *tselem = TREESTORE(te); - - if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { - outliner_item_select( - C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND); - } - - /* Look at its children. */ - if (TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) { - outliner_item_box_select(C, space_outliner, scene, rectf, te_sub, select); + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { + if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { + outliner_item_select( + C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND); } - } + }); } static int outliner_box_select_exec(bContext *C, wmOperator *op) @@ -1747,15 +1739,13 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); } WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); - LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - outliner_item_box_select(C, space_outliner, scene, &rectf, te, select); - } + outliner_box_select(C, space_outliner, &rectf, select); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index cc81d5ed68d..3b018d59881 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -84,6 +84,7 @@ #include "outliner_intern.hh" #include "tree/tree_element_rna.hh" #include "tree/tree_element_seq.hh" +#include "tree/tree_iterator.hh" static CLG_LogRef LOG = {"ed.outliner.tools"}; @@ -412,11 +413,10 @@ static void outliner_do_libdata_operation(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, - ListBase *lb, outliner_operation_fn operation_fn, void *user_data) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) || @@ -425,11 +425,7 @@ static void outliner_do_libdata_operation(bContext *C, operation_fn(C, reports, scene, te, tsep, tselem, user_data); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_libdata_operation( - C, reports, scene, space_outliner, &te->subtree, operation_fn, user_data); - } - } + }); } /** \} */ @@ -763,6 +759,10 @@ static void id_local_fn(bContext *C, struct OutlinerLibOverrideData { bool do_hierarchy; + + /** When creating new overrides, make them all user-editable. */ + bool do_fully_editable; + /** * For resync operation, force keeping newly created override IDs (or original linked IDs) * instead of re-applying relevant existing ID pointer property override operations. Helps @@ -957,7 +957,8 @@ static void id_override_library_create_fn(bContext *C, id_root_reference, id_hierarchy_root_reference, id_instance_hint, - &id_root_override); + &id_root_override, + data->do_fully_editable); BLI_assert(id_root_override != nullptr); BLI_assert(!ID_IS_LINKED(id_root_override)); @@ -1597,21 +1598,17 @@ static void outliner_do_data_operation( SpaceOutliner *space_outliner, int type, int event, - ListBase *lb, void (*operation_fn)(int, TreeElement *, TreeStoreElem *, void *), void *arg) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { operation_fn(event, te, tselem, arg); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_data_operation(space_outliner, type, event, &te->subtree, operation_fn, arg); - } - } + }); } static Base *outliner_batch_delete_hierarchy( @@ -1775,8 +1772,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) selection_changed = true; break; case OL_OP_REMAP: - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; @@ -1979,6 +1975,7 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, @@ -2024,6 +2021,12 @@ static const EnumPropertyItem prop_id_op_types[] = { "Make Library Override Hierarchy", "Make a local override of this linked data-block, and its hierarchy of dependencies - only " "applies to active Outliner item"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, + "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE", + 0, + "Make Library Override Hierarchy Fully Editable", + "Make a local override of this linked data-block, and its hierarchy of dependencies, making " + "them all fully user-editable - only applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, "OVERRIDE_LIBRARY_MAKE_EDITABLE", 0, @@ -2103,6 +2106,7 @@ static bool outliner_id_operation_item_poll(bContext *C, } return false; case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { return true; } @@ -2179,13 +2183,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_UNLINK: { /* unlink datablock from its parent */ if (objectlevel) { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_object_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_object_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr); ED_undo_push(C, "Unlink Object"); @@ -2194,61 +2193,36 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) switch (idlevel) { case ID_AC: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_action_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_action_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Unlink action"); break; case ID_MA: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_material_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_material_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr); ED_undo_push(C, "Unlink material"); break; case ID_TE: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_texture_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_texture_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr); ED_undo_push(C, "Unlink texture"); break; case ID_WO: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_world_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_world_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr); ED_undo_push(C, "Unlink world"); break; case ID_GR: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_collection_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_collection_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr); ED_undo_push(C, "Unlink Collection"); @@ -2261,20 +2235,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_LOCAL: { /* make local */ - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_local_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr); ED_undo_push(C, "Localized Data"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_create_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); ED_undo_push(C, "Overridden Data"); break; } @@ -2285,19 +2253,30 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) op->reports, scene, space_outliner, - &space_outliner->tree, id_override_library_create_hierarchy_pre_process_fn, &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); + id_override_library_create_hierarchy_post_process(C, &override_data); + + ED_undo_push(C, "Overridden Data Hierarchy"); + break; + } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_fully_editable = true; outliner_do_libdata_operation(C, op->reports, scene, space_outliner, - &space_outliner->tree, - id_override_library_create_fn, + id_override_library_create_hierarchy_pre_process_fn, &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); id_override_library_create_hierarchy_post_process(C, &override_data); - ED_undo_push(C, "Overridden Data Hierarchy"); + ED_undo_push(C, "Overridden Data Hierarchy Fully Editable"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { @@ -2305,7 +2284,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) op->reports, scene, space_outliner, - &space_outliner->tree, id_override_library_toggle_flag_fn, POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED)); @@ -2314,39 +2292,24 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_reset_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); ED_undo_push(C, "Reset Overridden Data"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_reset_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); ED_undo_push(C, "Reset Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_resync_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } @@ -2354,35 +2317,20 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; override_data.do_resync_hierarchy_enforce = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_resync_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_clear_hierarchy_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr); ED_undo_push(C, "Clear Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_clear_single_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr); ED_undo_push(C, "Clear Overridden Data Hierarchy"); break; } @@ -2390,26 +2338,16 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) /* make single user */ switch (idlevel) { case ID_AC: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - singleuser_action_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, singleuser_action_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Single-User Action"); break; case ID_WO: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - singleuser_world_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, singleuser_world_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr); ED_undo_push(C, "Single-User World"); @@ -2424,15 +2362,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_DELETE: { if (idlevel > 0) { outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); + C, op->reports, scene, space_outliner, id_delete_fn, nullptr); ED_undo_push(C, "Delete"); } break; } case OUTLINER_IDOP_REMAP: { if (idlevel > 0) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ } @@ -2455,13 +2392,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_FAKE_ADD: { /* set fake user */ - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_fake_user_set_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_fake_user_set_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Add Fake User"); @@ -2469,13 +2401,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_FAKE_CLEAR: { /* clear fake user */ - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_fake_user_clear_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_fake_user_clear_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Clear Fake User"); @@ -2484,20 +2411,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_RENAME: { /* rename */ outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); + C, op->reports, scene, space_outliner, item_rename_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Rename"); break; } case OUTLINER_IDOP_SELECT_LINKED: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_select_linked_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_select_linked_fn, nullptr); ED_outliner_select_sync_from_all_tag(C); ED_undo_push(C, "Select"); break; @@ -2580,21 +2502,19 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { case OL_LIB_DELETE: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr); ED_undo_push(C, "Delete Library"); break; } case OL_LIB_RELOCATE: { outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, lib_relocate_fn, nullptr); + C, op->reports, scene, space_outliner, lib_relocate_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; } case OL_LIB_RELOAD: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, lib_reload_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; @@ -2637,11 +2557,10 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) static void outliner_do_id_set_operation( SpaceOutliner *space_outliner, int type, - ListBase *lb, ID *newid, void (*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *)) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { @@ -2649,10 +2568,7 @@ static void outliner_do_id_set_operation( operation_fn(te, tselem, tsep, newid); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_id_set_operation(space_outliner, type, &te->subtree, newid, operation_fn); - } - } + }); } static void actionset_id_fn(TreeElement *UNUSED(te), @@ -2707,12 +2623,10 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) /* perform action if valid channel */ if (datalevel == TSE_ANIM_DATA) { - outliner_do_id_set_operation( - space_outliner, datalevel, &space_outliner->tree, (ID *)act, actionset_id_fn); + outliner_do_id_set_operation(space_outliner, datalevel, (ID *)act, actionset_id_fn); } else if (idlevel == ID_AC) { - outliner_do_id_set_operation( - space_outliner, idlevel, &space_outliner->tree, (ID *)act, actionset_id_fn); + outliner_do_id_set_operation(space_outliner, idlevel, (ID *)act, actionset_id_fn); } else { return OPERATOR_CANCELLED; @@ -2799,8 +2713,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) switch (event) { case OUTLINER_ANIMOP_CLEAR_ADT: /* Remove Animation Data - this may remove the active action, in some cases... */ - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, clear_animdata_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, clear_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Clear Animation Data"); @@ -2817,32 +2730,23 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) case OUTLINER_ANIMOP_CLEAR_ACT: /* clear active action - using standard rules */ - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, unlinkact_animdata_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, unlinkact_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Unlink action"); break; case OUTLINER_ANIMOP_REFRESH_DRV: - outliner_do_data_operation(space_outliner, - datalevel, - event, - &space_outliner->tree, - refreshdrivers_animdata_fn, - nullptr); + outliner_do_data_operation( + space_outliner, datalevel, event, refreshdrivers_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); // ED_undo_push(C, "Refresh Drivers"); /* No undo needed - shouldn't have any impact? */ break; case OUTLINER_ANIMOP_CLEAR_DRV: - outliner_do_data_operation(space_outliner, - datalevel, - event, - &space_outliner->tree, - cleardrivers_animdata_fn, - nullptr); + outliner_do_data_operation( + space_outliner, datalevel, event, cleardrivers_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); ED_undo_push(C, "Clear Drivers"); @@ -2892,8 +2796,7 @@ static int outliner_constraint_operation_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); eOutliner_PropConstraintOps event = (eOutliner_PropConstraintOps)RNA_enum_get(op->ptr, "type"); - outliner_do_data_operation( - space_outliner, TSE_CONSTRAINT, event, &space_outliner->tree, constraint_fn, C); + outliner_do_data_operation(space_outliner, TSE_CONSTRAINT, event, constraint_fn, C); if (event == OL_CONSTRAINTOP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2939,8 +2842,7 @@ static int outliner_modifier_operation_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); eOutliner_PropModifierOps event = (eOutliner_PropModifierOps)RNA_enum_get(op->ptr, "type"); - outliner_do_data_operation( - space_outliner, TSE_MODIFIER, event, &space_outliner->tree, modifier_fn, C); + outliner_do_data_operation(space_outliner, TSE_MODIFIER, event, modifier_fn, C); if (event == OL_MODIFIER_OP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2983,24 +2885,21 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) eOutliner_PropDataOps event = (eOutliner_PropDataOps)RNA_enum_get(op->ptr, "type"); switch (datalevel) { case TSE_POSE_CHANNEL: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, pchan_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, pchan_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "PoseChannel operation"); break; } case TSE_BONE: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, bone_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, bone_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "Bone operation"); break; } case TSE_EBONE: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, ebone_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, ebone_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "EditBone operation"); @@ -3008,16 +2907,14 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) } case TSE_SEQUENCE: { Scene *scene = CTX_data_scene(C); - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene); + outliner_do_data_operation(space_outliner, datalevel, event, sequence_fn, scene); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); ED_undo_push(C, "Sequencer operation"); break; } case TSE_GP_LAYER: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, gpencil_layer_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, gpencil_layer_fn, nullptr); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, nullptr); ED_undo_push(C, "Grease Pencil Layer operation"); @@ -3025,8 +2922,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) } case TSE_RNA_STRUCT: if (event == OL_DOP_SELECT_LINKED) { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, data_select_linked_fn, C); + outliner_do_data_operation(space_outliner, datalevel, event, data_select_linked_fn, C); } break; diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index bbd9b48c260..7b3ce499929 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -919,10 +919,6 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design"); } - if (tree_element_warnings_get(te, nullptr, nullptr)) { - te->flag |= TE_HAS_WARNING; - } - return te; } diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 4b947154864..0db612ce6db 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -27,6 +27,9 @@ #include "UI_view2d.h" #include "outliner_intern.hh" +#include "tree/tree_iterator.hh" + +using namespace blender::ed::outliner; /* -------------------------------------------------------------------- */ /** \name Tree View Context diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index 97dc659155f..5bcd1edebc0 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -438,7 +438,7 @@ static void outliner_deactivate(struct ScrArea *area) { /* Remove hover highlights */ SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); - outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY, false); + outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false); ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW)); } diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 141c68594e8..6ab497b3fbb 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -45,9 +45,9 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode( return nullptr; } -bool AbstractTreeDisplay::hasWarnings() const +bool AbstractTreeDisplay::supportsModeColumn() const { - return has_warnings; + return false; } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 327f29aa15e..190e35c81d6 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -75,12 +75,16 @@ class AbstractTreeDisplay { */ virtual ListBase buildTree(const TreeSourceData &source_data) = 0; - /** Accessor to whether given tree has some warnings to display. */ - bool hasWarnings() const; + /** + * Define if the display mode should be allowed to show a mode column on the left. This column + * adds an icon to indicate which objects are in the current mode (edit mode, pose mode, etc.) + * and allows adding other objects to the mode by clicking the icon. + * + * Returns false by default. + */ + virtual bool supportsModeColumn() const; protected: - bool has_warnings = false; - /** All derived classes will need a handle to this, so storing it in the base for convenience. */ SpaceOutliner &space_outliner_; }; @@ -100,6 +104,8 @@ class TreeDisplayViewLayer final : public AbstractTreeDisplay { ListBase buildTree(const TreeSourceData &source_data) override; + bool supportsModeColumn() const override; + private: void add_view_layer(Scene &, ListBase &, TreeElement *); void add_layer_collections_recursive(ListBase &, ListBase &, TreeElement &); @@ -212,6 +218,8 @@ class TreeDisplayScenes final : public AbstractTreeDisplay { TreeDisplayScenes(SpaceOutliner &space_outliner); ListBase buildTree(const TreeSourceData &source_data) override; + + bool supportsModeColumn() const override; }; /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index 476bbdb63ae..46a89f17687 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -136,9 +136,6 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0); tenlib->name = IFACE_("Current File"); } - if (tenlib->flag & TE_HAS_WARNING) { - has_warnings = true; - } } /* Create data-block list parent element on demand. */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc index 9e00a425a5a..6b1de7f8b95 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc @@ -26,6 +26,11 @@ TreeDisplayScenes::TreeDisplayScenes(SpaceOutliner &space_outliner) { } +bool TreeDisplayScenes::supportsModeColumn() const +{ + return true; +} + ListBase TreeDisplayScenes::buildTree(const TreeSourceData &source_data) { /* On first view we open scenes. */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index 19811e45b90..80b3365766a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -55,6 +55,11 @@ TreeDisplayViewLayer::TreeDisplayViewLayer(SpaceOutliner &space_outliner) { } +bool TreeDisplayViewLayer::supportsModeColumn() const +{ + return true; +} + ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 1e3fd2df7c2..94d55b70e3c 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -100,6 +100,11 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i return nullptr; } +StringRefNull AbstractTreeElement::getWarning() const +{ + return ""; +} + void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te) { if (!TREESTORE(legacy_te)->used) { @@ -118,39 +123,4 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner tree_element.expand(space_outliner); } -bool tree_element_warnings_get(TreeElement *te, int *r_icon, const char **r_message) -{ - TreeStoreElem *tselem = te->store_elem; - - if (tselem->type != TSE_SOME_ID) { - return false; - } - if (te->idcode != ID_LI) { - return false; - } - - Library *library = (Library *)tselem->id; - if (library->tag & LIBRARY_TAG_RESYNC_REQUIRED) { - if (r_icon) { - *r_icon = ICON_ERROR; - } - if (r_message) { - *r_message = TIP_( - "Contains linked library overrides that need to be resynced, updating the library is " - "recommended"); - } - return true; - } - if (library->id.tag & LIB_TAG_MISSING) { - if (r_icon) { - *r_icon = ICON_ERROR; - } - if (r_message) { - *r_message = TIP_("Missing library"); - } - return true; - } - return false; -} - } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index c6593a517dd..0dcd75d340d 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -8,6 +8,8 @@ #include <memory> +#include "BLI_string_ref.hh" + struct ListBase; struct SpaceOutliner; struct TreeElement; @@ -56,6 +58,12 @@ class AbstractTreeElement { } /** + * By letting this return a warning message, the tree element will display a warning icon with + * the message in the tooltip. + */ + virtual StringRefNull getWarning() const; + + /** * Expand this tree element if it is displayed for the first time (as identified by its * tree-store element). * @@ -96,13 +104,4 @@ struct TreeElement *outliner_add_element(SpaceOutliner *space_outliner, void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner); -/** - * Get actual warning data of a tree element, if any. - * - * \param r_icon: The icon to display as warning. - * \param r_message: The message to display as warning. - * \return true if there is a warning, false otherwise. - */ -bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message); - } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc index 0dcaec0385a..4f1b951ccaf 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc @@ -4,6 +4,8 @@ * \ingroup spoutliner */ +#include "BLT_translation.h" + #include "DNA_ID.h" #include "DNA_listBase.h" @@ -24,4 +26,21 @@ bool TreeElementIDLibrary::isExpandValid() const return true; } +StringRefNull TreeElementIDLibrary::getWarning() const +{ + Library &library = reinterpret_cast<Library &>(id_); + + if (library.tag & LIBRARY_TAG_RESYNC_REQUIRED) { + return TIP_( + "Contains linked library overrides that need to be resynced, updating the library is " + "recommended"); + } + + if (library.id.tag & LIB_TAG_MISSING) { + return TIP_("Missing library"); + } + + return {}; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh index ed599cf04da..2d89b55813f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh @@ -17,6 +17,8 @@ class TreeElementIDLibrary final : public TreeElementID { TreeElementIDLibrary(TreeElement &legacy_te, Library &library); bool isExpandValid() const override; + + blender::StringRefNull getWarning() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 3a039da86c2..53e7b88c923 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -38,6 +38,19 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i } } +StringRefNull TreeElementOverridesBase::getWarning() const +{ + if (id.flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { + return TIP_("This override data-block is not needed anymore, but was detected as user-edited"); + } + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&id) && ID_REAL_USERS(&id) == 0) { + return TIP_("This override data-block is unused"); + } + + return {}; +} + void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const { BLI_assert(id.override_library != nullptr); @@ -93,4 +106,15 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t legacy_te.name = override_data.override_property.rna_path; } +StringRefNull TreeElementOverridesProperty::getWarning() const +{ + if (!is_rna_path_valid) { + return TIP_( + "This override property does not exist in current data, it will be removed on " + "next .blend file save"); + } + + return {}; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index b42e1c37a0f..1db46d9af1d 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -34,6 +34,8 @@ class TreeElementOverridesBase final : public AbstractTreeElement { TreeElementOverridesBase(TreeElement &legacy_te, ID &id); void expand(SpaceOutliner &) const override; + + StringRefNull getWarning() const override; }; class TreeElementOverridesProperty final : public AbstractTreeElement { @@ -46,6 +48,8 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { public: TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); + + StringRefNull getWarning() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.cc b/source/blender/editors/space_outliner/tree/tree_iterator.cc new file mode 100644 index 00000000000..85ff9e6437e --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_iterator.cc @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_space_types.h" + +#include "BLI_listbase.h" + +#include "../outliner_intern.hh" + +#include "tree_iterator.hh" + +namespace blender::ed::outliner::tree_iterator { + +void all(const SpaceOutliner &space_outliner, const VisitorFn visitor) +{ + all_open(space_outliner, space_outliner.tree, visitor); +} + +void all(const ListBase &subtree, const VisitorFn visitor) +{ + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + const ListBase subtree = element->subtree; + + visitor(element); + /* Don't access element from now on, it may be freed. */ + + all(subtree, visitor); + } +} + +void all_open(const SpaceOutliner &space_outliner, const VisitorFn visitor) +{ + all_open(space_outliner, space_outliner.tree, visitor); +} + +void all_open(const SpaceOutliner &space_outliner, + const ListBase &subtree, + const VisitorFn visitor) +{ + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + const bool is_open = TSELEM_OPEN(element->store_elem, &space_outliner); + const ListBase subtree = element->subtree; + + visitor(element); + /* Don't access element from now on, it may be freed. */ + + if (is_open) { + all_open(space_outliner, subtree, visitor); + } + } +} + +} // namespace blender::ed::outliner::tree_iterator diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh new file mode 100644 index 00000000000..e3b3c90eaad --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include "BLI_function_ref.hh" + +struct ListBase; +struct SpaceOutliner; +struct TreeElement; + +namespace blender::ed::outliner { +namespace tree_iterator { + +using VisitorFn = FunctionRef<void(TreeElement *)>; + +/** + * Preorder (meaning depth-first) traversal of all elements (regardless of collapsed state). + * Freeing the currently visited element in \a visitor is fine. + */ +void all(const SpaceOutliner &space_outliner, VisitorFn visitor); +void all(const ListBase &subtree, VisitorFn visitor); + +/** + * Preorder (meaning depth-first) traversal of all elements not part of a collapsed sub-tree. + * Freeing the currently visited element in \a visitor is fine. + */ +void all_open(const SpaceOutliner &, VisitorFn visitor); +void all_open(const SpaceOutliner &, const ListBase &subtree, VisitorFn visitor); + +} // namespace tree_iterator +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a02058fde6b..48abe71e35f 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -590,16 +590,15 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *view3d_mat_drop_tooltip(bContext *C, wmDrag *drag, const int xy[2], - struct wmDropBox *drop) + wmDropBox *UNUSED(drop)) { const char *name = WM_drag_get_item_name(drag); ARegion *region = CTX_wm_region(C); - RNA_string_set(drop->ptr, "name", name); int mval[2] = { xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin, }; - return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval); + return ED_object_ot_drop_named_material_tooltip(C, name, mval); } static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -716,7 +715,7 @@ static void view3d_ob_drop_copy_local_id(bContext *UNUSED(C), wmDrag *drag, wmDr { ID *id = WM_drag_get_local_ID(drag, ID_OB); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", id->session_uuid); /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ BLI_assert(drag->type != WM_DRAG_ASSET); @@ -751,7 +750,7 @@ static void view3d_ob_drop_copy_external_asset(bContext *UNUSED(C), wmDrag *drag DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", id->session_uuid); Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); if (base != NULL) { @@ -823,15 +822,15 @@ static void view3d_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *dr { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); } static void view3d_id_drop_copy_with_type(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); RNA_enum_set(drop->ptr, "type", GS(id->name)); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); } static void view3d_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) @@ -839,7 +838,7 @@ static void view3d_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBo ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { - RNA_string_set(drop->ptr, "name", id->name + 2); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); RNA_struct_property_unset(drop->ptr, "filepath"); } else if (drag->path[0]) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index e52ff062302..9f8d7afd9a8 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -608,7 +608,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign"); + WM_operator_properties_id_lookup(ot, true); WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, @@ -678,10 +678,8 @@ static int drop_world_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - - RNA_string_get(op->ptr, "name", name); - World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name); + World *world = (World *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_WO); if (world == NULL) { return OPERATOR_CANCELLED; } @@ -718,7 +716,7 @@ void VIEW3D_OT_drop_world(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index cbc2cab0a7a..deae51a1149 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -197,11 +197,7 @@ void recalcData_sequencer_image(TransInfo *t) /* Rotation. Scaling can cause negative rotation. */ if (t->mode == TFM_ROTATION) { - const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)}; - float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1]; - transform->rotation = tdseq->orig_rotation + rotation; - transform->rotation += DEG2RAD(360.0); - transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); + transform->rotation = tdseq->orig_rotation - t->values_final[0]; } SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); } @@ -209,9 +205,6 @@ void recalcData_sequencer_image(TransInfo *t) void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t) { - if (t->state == TRANS_CANCEL) { - return; - } TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); TransData *td = NULL; @@ -225,8 +218,14 @@ void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo * TransDataSeq *tdseq = td->extra; Sequence *seq = tdseq->seq; StripTransform *transform = seq->strip->transform; - Scene *scene = t->scene; + if (t->state == TRANS_CANCEL) { + if (t->mode == TFM_ROTATION) { + transform->rotation = tdseq->orig_rotation; + } + continue; + } + Scene *scene = t->scene; RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr); if (t->mode == TFM_ROTATION) { diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index c0ea753ed51..84dca352c9f 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -103,7 +103,7 @@ static bool ED_uvedit_ensure_uvs(Object *obedit) int cd_loop_uv_offset; if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) { - ED_mesh_uv_texture_add(obedit->data, NULL, true, true, NULL); + ED_mesh_uv_add(obedit->data, NULL, true, true, NULL); } /* Happens when there are no faces. */ @@ -339,7 +339,6 @@ static ParamHandle *construct_param_handle(const Scene *scene, const UnwrapOptions *options, UnwrapResultInfo *result_info) { - ParamHandle *handle; BMFace *efa; BMLoop *l; BMEdge *eed; @@ -348,7 +347,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { float aspx, aspy; @@ -417,14 +416,13 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, const UnwrapOptions *options, int *count_fail) { - ParamHandle *handle; BMFace *efa; BMLoop *l; BMEdge *eed; BMIter iter, liter; int i; - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { Object *ob = objects[0]; @@ -539,7 +537,6 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, const UnwrapOptions *options, UnwrapResultInfo *result_info) { - ParamHandle *handle; /* index pointers */ MPoly *mpoly; MLoop *mloop; @@ -571,7 +568,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { float aspx, aspy; @@ -1023,8 +1020,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) bool rotate = true; bool ignore_pinned = false; - ParamHandle *handle; - handle = construct_param_handle(scene, ob, bm, &options, NULL); + ParamHandle *handle = construct_param_handle(scene, ob, bm, &options, NULL); GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1043,8 +1039,7 @@ static void uvedit_pack_islands_multi(const Scene *scene, bool rotate, bool ignore_pinned) { - ParamHandle *handle; - handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL); + ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL); GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -3038,7 +3033,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) * since we are not in edit mode we need to ensure only the uv flags are tested */ scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION; - ED_mesh_uv_texture_ensure(me, NULL); + ED_mesh_uv_ensure(me, NULL); BM_mesh_bm_from_me(bm, me, diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index a5194883cf2..624b0695aa3 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -12,8 +12,8 @@ extern "C" { #endif -typedef void ParamHandle; /* handle to a set of charts */ -typedef intptr_t ParamKey; /* (hash) key for identifying verts and faces */ +typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */ +typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ typedef enum ParamBool { PARAM_TRUE = 1, PARAM_FALSE = 0, diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index e25fff0d6b8..ad4b051a6c2 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -59,7 +59,6 @@ typedef struct PHash { struct PChart; struct PEdge; struct PFace; -struct PHandle; struct PVert; /* Simplices */ @@ -171,7 +170,7 @@ typedef struct PChart { } u; uchar flag; - struct PHandle *handle; + ParamHandle *handle; } PChart; enum PChartFlag { @@ -185,7 +184,7 @@ enum PHandleState { PHANDLE_STATE_STRETCH, }; -typedef struct PHandle { +typedef struct ParamHandle { enum PHandleState state; MemArena *arena; MemArena *polyfill_arena; @@ -204,7 +203,7 @@ typedef struct PHandle { RNG *rng; float blend; bool do_aspect; -} PHandle; +} ParamHandle; /* PHash * - special purpose hash that keeps all its elements in a single linked list. @@ -637,7 +636,7 @@ static void p_chart_topological_sanity_check(PChart *chart) /* Loading / Flushing */ -static void p_vert_load_pin_select_uvs(PHandle *handle, PVert *v) +static void p_vert_load_pin_select_uvs(ParamHandle *handle, PVert *v) { PEdge *e; int nedges = 0, npins = 0; @@ -679,7 +678,7 @@ static void p_vert_load_pin_select_uvs(PHandle *handle, PVert *v) } } -static void p_flush_uvs(PHandle *handle, PChart *chart) +static void p_flush_uvs(ParamHandle *handle, PChart *chart) { PEdge *e; @@ -691,7 +690,7 @@ static void p_flush_uvs(PHandle *handle, PChart *chart) } } -static void p_flush_uvs_blend(PHandle *handle, PChart *chart, float blend) +static void p_flush_uvs_blend(ParamHandle *handle, PChart *chart, float blend) { PEdge *e; float invblend = 1.0f - blend; @@ -742,7 +741,7 @@ static void p_face_restore_uvs(PFace *f) /* Construction (use only during construction, relies on u.key being set */ -static PVert *p_vert_add(PHandle *handle, PHashKey key, const float co[3], PEdge *e) +static PVert *p_vert_add(ParamHandle *handle, PHashKey key, const float co[3], PEdge *e) { PVert *v = (PVert *)BLI_memarena_alloc(handle->arena, sizeof(*v)); copy_v3_v3(v->co, co); @@ -765,7 +764,7 @@ static PVert *p_vert_add(PHandle *handle, PHashKey key, const float co[3], PEdge return v; } -static PVert *p_vert_lookup(PHandle *handle, PHashKey key, const float co[3], PEdge *e) +static PVert *p_vert_lookup(ParamHandle *handle, PHashKey key, const float co[3], PEdge *e) { PVert *v = (PVert *)phash_lookup(handle->hash_verts, key); @@ -789,7 +788,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v) return nv; } -static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys) +static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys) { PHashKey key = PHASH_edge(vkeys[0], vkeys[1]); PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key); @@ -808,9 +807,8 @@ static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys) return NULL; } -static int p_face_exists(ParamHandle *phandle, ParamKey *pvkeys, int i1, int i2, int i3) +static int p_face_exists(ParamHandle *handle, ParamKey *pvkeys, int i1, int i2, int i3) { - PHandle *handle = (PHandle *)phandle; PHashKey *vkeys = (PHashKey *)pvkeys; PHashKey key = PHASH_edge(vkeys[i1], vkeys[i2]); PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key); @@ -833,7 +831,7 @@ static int p_face_exists(ParamHandle *phandle, ParamKey *pvkeys, int i1, int i2, return P_FALSE; } -static PChart *p_chart_new(PHandle *handle) +static PChart *p_chart_new(ParamHandle *handle) { PChart *chart = (PChart *)MEM_callocN(sizeof(*chart), "PChart"); chart->handle = handle; @@ -881,7 +879,10 @@ static PBool p_edge_implicit_seam(PEdge *e, PEdge *ep) return P_FALSE; } -static PBool p_edge_has_pair(PHandle *handle, PEdge *e, PBool topology_from_uvs, PEdge **r_pair) +static PBool p_edge_has_pair(ParamHandle *handle, + PEdge *e, + PBool topology_from_uvs, + PEdge **r_pair) { PHashKey key; PEdge *pe; @@ -930,7 +931,7 @@ static PBool p_edge_has_pair(PHandle *handle, PEdge *e, PBool topology_from_uvs, return (*r_pair != NULL); } -static PBool p_edge_connect_pair(PHandle *handle, +static PBool p_edge_connect_pair(ParamHandle *handle, PEdge *e, PBool topology_from_uvs, PEdge ***stack) @@ -954,7 +955,7 @@ static PBool p_edge_connect_pair(PHandle *handle, return (e->pair != NULL); } -static int p_connect_pairs(PHandle *handle, PBool topology_from_uvs) +static int p_connect_pairs(ParamHandle *handle, PBool topology_from_uvs) { PEdge **stackbase = MEM_mallocN(sizeof(*stackbase) * phash_size(handle->hash_faces), "Pstackbase"); @@ -1061,7 +1062,7 @@ static void p_split_vert(PChart *chart, PEdge *e) } } -static PChart **p_split_charts(PHandle *handle, PChart *chart, int ncharts) +static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts) { PChart **charts = MEM_mallocN(sizeof(*charts) * ncharts, "PCharts"), *nchart; PFace *f, *nextf; @@ -1100,7 +1101,7 @@ static PChart **p_split_charts(PHandle *handle, PChart *chart, int ncharts) return charts; } -static PFace *p_face_add(PHandle *handle) +static PFace *p_face_add(ParamHandle *handle) { PFace *f; PEdge *e1, *e2, *e3; @@ -1132,7 +1133,7 @@ static PFace *p_face_add(PHandle *handle) return f; } -static PFace *p_face_add_construct(PHandle *handle, +static PFace *p_face_add_construct(ParamHandle *handle, ParamKey key, const ParamKey *vkeys, float *co[4], @@ -1219,7 +1220,7 @@ static PFace *p_face_add_fill(PChart *chart, PVert *v1, PVert *v2, PVert *v3) return f; } -static PBool p_quad_split_direction(PHandle *handle, float **co, PHashKey *vkeys) +static PBool p_quad_split_direction(ParamHandle *handle, float **co, PHashKey *vkeys) { /* Slight bias to prefer one edge over the other in case they are equal, so * that in symmetric models we choose the same split direction instead of @@ -3212,7 +3213,7 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf) } } -static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart) +static PBool p_chart_lscm_solve(ParamHandle *handle, PChart *chart) { LinearSolver *context = chart->u.lscm.context; PVert *v, *pin1 = chart->u.lscm.pin1, *pin2 = chart->u.lscm.pin2; @@ -4361,7 +4362,7 @@ static void p_smooth(PChart *chart) ParamHandle *GEO_uv_parametrizer_construct_begin(void) { - PHandle *handle = MEM_callocN(sizeof(*handle), "PHandle"); + ParamHandle *handle = MEM_callocN(sizeof(*handle), "ParamHandle"); handle->construction_chart = p_chart_new(handle); handle->state = PHANDLE_STATE_ALLOCATED; handle->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "param construct arena"); @@ -4375,21 +4376,18 @@ ParamHandle *GEO_uv_parametrizer_construct_begin(void) handle->hash_edges = phash_new((PHashLink **)&handle->construction_chart->edges, 1); handle->hash_faces = phash_new((PHashLink **)&handle->construction_chart->faces, 1); - return (ParamHandle *)handle; + return handle; } -void GEO_uv_parametrizer_aspect_ratio(ParamHandle *handle, float aspx, float aspy) +void GEO_uv_parametrizer_aspect_ratio(ParamHandle *phandle, float aspx, float aspy) { - PHandle *phandle = (PHandle *)handle; - phandle->aspx = aspx; phandle->aspy = aspy; phandle->do_aspect = true; } -void GEO_uv_parametrizer_delete(ParamHandle *handle) +void GEO_uv_parametrizer_delete(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; int i; param_assert(ELEM(phandle->state, PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED)); @@ -4426,9 +4424,8 @@ static void p_add_ngon(ParamHandle *handle, ParamBool *select) { /* Allocate memory for polyfill. */ - PHandle *phandle = (PHandle *)handle; - MemArena *arena = phandle->polyfill_arena; - Heap *heap = phandle->polyfill_heap; + MemArena *arena = handle->polyfill_arena; + Heap *heap = handle->polyfill_heap; uint nfilltri = nverts - 2; uint(*tris)[3] = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri); float(*projverts)[2] = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts); @@ -4478,7 +4475,7 @@ static void p_add_ngon(ParamHandle *handle, BLI_memarena_clear(arena); } -void GEO_uv_parametrizer_face_add(ParamHandle *handle, +void GEO_uv_parametrizer_face_add(ParamHandle *phandle, ParamKey key, int nverts, ParamKey *vkeys, @@ -4487,15 +4484,13 @@ void GEO_uv_parametrizer_face_add(ParamHandle *handle, ParamBool *pin, ParamBool *select) { - PHandle *phandle = (PHandle *)handle; - param_assert(phash_lookup(phandle->hash_faces, key) == NULL); param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); param_assert(ELEM(nverts, 3, 4)); if (nverts > 4) { /* ngon */ - p_add_ngon(handle, key, nverts, vkeys, co, uv, pin, select); + p_add_ngon(phandle, key, nverts, vkeys, co, uv, pin, select); } else if (nverts == 4) { /* quad */ @@ -4514,9 +4509,8 @@ void GEO_uv_parametrizer_face_add(ParamHandle *handle, } } -void GEO_uv_parametrizer_edge_set_seam(ParamHandle *handle, ParamKey *vkeys) +void GEO_uv_parametrizer_edge_set_seam(ParamHandle *phandle, ParamKey *vkeys) { - PHandle *phandle = (PHandle *)handle; PEdge *e; param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); @@ -4527,12 +4521,11 @@ void GEO_uv_parametrizer_edge_set_seam(ParamHandle *handle, ParamKey *vkeys) } } -void GEO_uv_parametrizer_construct_end(ParamHandle *handle, +void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, ParamBool fill, ParamBool topology_from_uvs, int *count_fail) { - PHandle *phandle = (PHandle *)handle; PChart *chart = phandle->construction_chart; int i, j, nboundaries = 0; PEdge *outer; @@ -4572,7 +4565,7 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *handle, } for (v = chart->verts; v; v = v->nextlink) { - p_vert_load_pin_select_uvs(handle, v); + p_vert_load_pin_select_uvs(phandle, v); } } @@ -4581,9 +4574,8 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *handle, phandle->state = PHANDLE_STATE_CONSTRUCTED; } -void GEO_uv_parametrizer_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf) +void GEO_uv_parametrizer_lscm_begin(ParamHandle *phandle, ParamBool live, ParamBool abf) { - PHandle *phandle = (PHandle *)handle; PFace *f; int i; @@ -4598,9 +4590,8 @@ void GEO_uv_parametrizer_lscm_begin(ParamHandle *handle, ParamBool live, ParamBo } } -void GEO_uv_parametrizer_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed) +void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, int *count_failed) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4638,9 +4629,8 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *handle, int *count_changed, int } } -void GEO_uv_parametrizer_lscm_end(ParamHandle *handle) +void GEO_uv_parametrizer_lscm_end(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; int i; param_assert(phandle->state == PHANDLE_STATE_LSCM); @@ -4655,9 +4645,8 @@ void GEO_uv_parametrizer_lscm_end(ParamHandle *handle) phandle->state = PHANDLE_STATE_CONSTRUCTED; } -void GEO_uv_parametrizer_stretch_begin(ParamHandle *handle) +void GEO_uv_parametrizer_stretch_begin(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; PVert *v; PFace *f; @@ -4685,17 +4674,14 @@ void GEO_uv_parametrizer_stretch_begin(ParamHandle *handle) } } -void GEO_uv_parametrizer_stretch_blend(ParamHandle *handle, float blend) +void GEO_uv_parametrizer_stretch_blend(ParamHandle *phandle, float blend) { - PHandle *phandle = (PHandle *)handle; - param_assert(phandle->state == PHANDLE_STATE_STRETCH); phandle->blend = blend; } -void GEO_uv_parametrizer_stretch_iter(ParamHandle *handle) +void GEO_uv_parametrizer_stretch_iter(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4707,10 +4693,8 @@ void GEO_uv_parametrizer_stretch_iter(ParamHandle *handle) } } -void GEO_uv_parametrizer_stretch_end(ParamHandle *handle) +void GEO_uv_parametrizer_stretch_end(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; - param_assert(phandle->state == PHANDLE_STATE_STRETCH); phandle->state = PHANDLE_STATE_CONSTRUCTED; @@ -4718,9 +4702,8 @@ void GEO_uv_parametrizer_stretch_end(ParamHandle *handle) phandle->rng = NULL; } -void GEO_uv_parametrizer_smooth_area(ParamHandle *handle) +void GEO_uv_parametrizer_smooth_area(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; int i; param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED); @@ -4738,13 +4721,11 @@ void GEO_uv_parametrizer_smooth_area(ParamHandle *handle) } /* don't pack, just rotate (used for better packing) */ -static void GEO_uv_parametrizer_pack_rotate(ParamHandle *handle, bool ignore_pinned) +static void GEO_uv_parametrizer_pack_rotate(ParamHandle *phandle, bool ignore_pinned) { PChart *chart; int i; - PHandle *phandle = (PHandle *)handle; - for (i = 0; i < phandle->ncharts; i++) { chart = phandle->charts[i]; @@ -4770,9 +4751,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, float trans[2]; double area = 0.0; - PHandle *phandle = (PHandle *)handle; - - if (phandle->ncharts == 0) { + if (handle->ncharts == 0) { return; } @@ -4781,15 +4760,15 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, GEO_uv_parametrizer_pack_rotate(handle, ignore_pinned); } - if (phandle->aspx != phandle->aspy) { - GEO_uv_parametrizer_scale(handle, 1.0f / phandle->aspx, 1.0f / phandle->aspy); + if (handle->aspx != handle->aspy) { + GEO_uv_parametrizer_scale(handle, 1.0f / handle->aspx, 1.0f / handle->aspy); } /* we may not use all these boxes */ - boxarray = MEM_mallocN(phandle->ncharts * sizeof(BoxPack), "BoxPack box"); + boxarray = MEM_mallocN(handle->ncharts * sizeof(BoxPack), "BoxPack box"); - for (i = 0; i < phandle->ncharts; i++) { - chart = phandle->charts[i]; + for (i = 0; i < handle->ncharts; i++) { + chart = handle->charts[i]; if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { unpacked++; @@ -4821,8 +4800,8 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, * 0.0 to 1.0 but not give a massive margin */ margin = (margin * (float)area) * 0.1f; unpacked = 0; - for (i = 0; i < phandle->ncharts; i++) { - chart = phandle->charts[i]; + for (i = 0; i < handle->ncharts; i++) { + chart = handle->charts[i]; if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { unpacked++; @@ -4838,7 +4817,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, } } - BLI_box_pack_2d(boxarray, phandle->ncharts - unpacked, &tot_width, &tot_height); + BLI_box_pack_2d(boxarray, handle->ncharts - unpacked, &tot_width, &tot_height); if (tot_height > tot_width) { scale = tot_height != 0.0f ? (1.0f / tot_height) : 1.0f; @@ -4847,30 +4826,29 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, scale = tot_width != 0.0f ? (1.0f / tot_width) : 1.0f; } - for (i = 0; i < phandle->ncharts - unpacked; i++) { + for (i = 0; i < handle->ncharts - unpacked; i++) { box = boxarray + i; trans[0] = box->x; trans[1] = box->y; - chart = phandle->charts[box->index]; + chart = handle->charts[box->index]; p_chart_uv_translate(chart, trans); p_chart_uv_scale(chart, scale); } MEM_freeN(boxarray); - if (phandle->aspx != phandle->aspy) { - GEO_uv_parametrizer_scale(handle, phandle->aspx, phandle->aspy); + if (handle->aspx != handle->aspy) { + GEO_uv_parametrizer_scale(handle, handle->aspx, handle->aspy); } } -void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned) +void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) { PChart *chart; int i; float tot_uvarea = 0.0f, tot_facearea = 0.0f; float tot_fac, fac; float minv[2], maxv[2], trans[2]; - PHandle *phandle = (PHandle *)handle; if (phandle->ncharts == 0) { return; @@ -4930,9 +4908,8 @@ void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned) } } -void GEO_uv_parametrizer_scale(ParamHandle *handle, float x, float y) +void GEO_uv_parametrizer_scale(ParamHandle *phandle, float x, float y) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4942,9 +4919,8 @@ void GEO_uv_parametrizer_scale(ParamHandle *handle, float x, float y) } } -void GEO_uv_parametrizer_flush(ParamHandle *handle) +void GEO_uv_parametrizer_flush(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4964,9 +4940,8 @@ void GEO_uv_parametrizer_flush(ParamHandle *handle) } } -void GEO_uv_parametrizer_flush_restore(ParamHandle *handle) +void GEO_uv_parametrizer_flush_restore(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; PFace *f; int i; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index 930ebb78b46..88515d849bc 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -109,7 +109,7 @@ static void reduce_stroke_points(bGPdata *gpd, const int points_num, const eBuildGpencil_Transition transition) { - if (points_num == 0) { + if ((points_num == 0) || (gps->points == NULL)) { clear_stroke(gpf, gps); return; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c index c576cfbe525..414231fcae5 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c @@ -122,6 +122,30 @@ static int remapTime(struct GpencilModifierData *md, nfra = (efra + 1 - (cfra + offset - 1) % (efra - sfra + 1)) - 1; } } + + if (mmd->mode == GP_TIME_MODE_PINGPONG) { + if ((mmd->flag & GP_TIME_KEEP_LOOP) == 0) { + if (((int)(cfra + offset - 1) / (efra - sfra)) % (2)) { + nfra = efra - (cfra + offset - 1) % (efra - sfra); + } + else { + nfra = sfra + (cfra + offset - 1) % (efra - sfra); + } + if (cfra > (efra - sfra) * 2) { + nfra = sfra + offset; + } + } + else { + + if (((int)(cfra + offset - 1) / (efra - sfra)) % (2)) { + nfra = efra - (cfra + offset - 1) % (efra - sfra); + } + else { + nfra = sfra + (cfra + offset - 1) % (efra - sfra); + } + } + } + return nfra; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc index 174399618a5..5e741ccbd55 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc @@ -21,5 +21,5 @@ static bool cmp_adjacent_items(const LineartAdjacentEdge &p1, const LineartAdjac void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length) { - blender::parallel_sort(ai, ai + length - 1, cmp_adjacent_items); + blender::parallel_sort(ai, ai + length, cmp_adjacent_items); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index fe1bbc3fc23..016b70cedb0 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1753,6 +1753,11 @@ static void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, static void lineart_finalize_object_edge_array(LineartPendingEdges *pe, LineartObjectInfo *obi) { + /* In case of line art "occlusion only" or contour not enabled, it's possible for an object to + * not produce any feature lines. */ + if (!obi->pending_edges.array) { + return; + } memcpy(&pe->array[pe->next], obi->pending_edges.array, sizeof(LineartEdge *) * obi->pending_edges.next); @@ -2357,6 +2362,11 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, } if (ob->type == OB_MESH) { use_mesh = BKE_object_get_evaluated_mesh(ob); + if (use_mesh->edit_mesh) { + /* If the object is being edited, then the mesh is not evaluated fully into the final + * result, do not load them. */ + return; + } } else { use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index ec9078983f9..e78c86ae196 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -406,8 +406,6 @@ set(GLSL_SRC shaders/gpu_shader_cfg_world_clip_lib.glsl shaders/gpu_shader_colorspace_lib.glsl - shaders/gpu_shader_common_obinfos_lib.glsl - GPU_shader_shared_utils.h ) diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 061b850619f..7fe467de402 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -35,7 +35,7 @@ int GPU_max_compute_shader_storage_blocks(void); int GPU_extensions_len(void); const char *GPU_extension_get(int i); -int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size); +int GPU_texture_size_with_limit(int res); bool GPU_mip_render_workaround(void); bool GPU_depth_blitting_workaround(void); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index 4ec215c2d3b..eb69a1d2635 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -33,11 +33,10 @@ int GPU_max_texture_size() return GCaps.max_texture_size; } -int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size) +int GPU_texture_size_with_limit(int res) { int size = GPU_max_texture_size(); - int reslimit = (limit_gl_texture_size && (U.glreslimit != 0)) ? min_ii(U.glreslimit, size) : - size; + int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; return min_ii(reslimit, res); } diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 1cd2301aa4e..0c796ddc765 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -291,26 +291,12 @@ static void detect_workarounds() } /* See T82856: AMD drivers since 20.11 running on a polaris architecture doesn't support the * `GL_INT_2_10_10_10_REV` data type correctly. This data type is used to pack normals and flags. - * The work around uses `GPU_RGBA16I`. + * The work around uses `GPU_RGBA16I`. In 22.?.? drivers this has been fixed for + * polaris platform. Keeping legacy platforms around just in case. */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) { - const Vector<std::string> matches = {"RX 460", - "RX 470", - "RX 480", - "RX 490", - "RX 560", - "RX 560X", - "RX 570", - "RX 580", - "RX 580X", - "RX 590", - "RX550/550", - "(TM) 520", - "(TM) 530", - "(TM) 535", - "R5", - "R7", - "R9"}; + const Vector<std::string> matches = { + "RX550/550", "(TM) 520", "(TM) 530", "(TM) 535", "R5", "R7", "R9"}; if (match_renderer(renderer, matches)) { GCaps.use_hq_normals_workaround = true; diff --git a/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl b/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl deleted file mode 100644 index f5b6de4899f..00000000000 --- a/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl +++ /dev/null @@ -1,23 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -#ifndef GPU_OBINFOS_UBO -# define GPU_OBINFOS_UBO -struct ObjectInfos { - vec4 drw_OrcoTexCoFactors[2]; - vec4 drw_ObjectColor; - vec4 drw_Infos; -}; - -# ifndef USE_GPU_SHADER_CREATE_INFO -layout(std140) uniform infoBlock -{ - /* DRW_RESOURCE_CHUNK_LEN = 512 */ - ObjectInfos drw_infos[512]; -}; -# endif - -# define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) -# define ObjectInfo (drw_infos[resource_id].drw_Infos) -# define ObjectColor (drw_infos[resource_id].drw_ObjectColor) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_blackbody.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_blackbody.glsl index d0111aa3839..e257483f364 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_blackbody.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_blackbody.glsl @@ -1,13 +1,5 @@ void node_blackbody(float temperature, sampler1DArray spectrummap, float layer, out vec4 color) { - if (temperature >= 12000.0) { - color = vec4(0.826270103, 0.994478524, 1.56626022, 1.0); - } - else if (temperature < 965.0) { - color = vec4(4.70366907, 0.0, 0.0, 1.0); - } - else { - float t = (temperature - 965.0) / (12000.0 - 965.0); - color = vec4(texture(spectrummap, vec2(t, layer)).rgb, 1.0); - } + float t = (temperature - 800.0) / (12000.0 - 800.0); + color = vec4(texture(spectrummap, vec2(t, layer)).rgb, 1.0); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl index e219e2b9bbe..a54dc59ddfe 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl @@ -1,3 +1,5 @@ + +#ifdef OBINFO_LIB void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal) { if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) { @@ -10,6 +12,7 @@ void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal) outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N; outnormal = normalize(outnormal); } +#endif void color_to_normal_new_shading(vec3 color, out vec3 normal) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl index 2c5d38eabbe..48d627dcd0b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl @@ -1,15 +1,7 @@ -void node_wavelength(float wavelength, - sampler1DArray spectrummap, - float layer, - vec3 xyz_to_r, - vec3 xyz_to_g, - vec3 xyz_to_b, - out vec4 color) +void node_wavelength(float wavelength, sampler1DArray spectrummap, float layer, out vec4 color) { - mat3 xyz_to_rgb = mat3(xyz_to_r, xyz_to_g, xyz_to_b); float t = (wavelength - 380.0) / (780.0 - 380.0); - vec3 xyz = texture(spectrummap, vec2(t, layer)).rgb; - vec3 rgb = xyz * xyz_to_rgb; + vec3 rgb = texture(spectrummap, vec2(t, layer)).rgb; rgb *= 1.0 / 2.52; /* Empirical scale from lg to make all comps <= 1. */ color = vec4(clamp(rgb, 0.0, 1.0), 1.0); } diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 97030d44047..512dc674fc7 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -54,6 +54,7 @@ bool IMB_colormanagement_space_is_scene_linear(struct ColorSpace *colorspace); bool IMB_colormanagement_space_is_srgb(struct ColorSpace *colorspace); bool IMB_colormanagement_space_name_is_data(const char *name); bool IMB_colormanagement_space_name_is_scene_linear(const char *name); +bool IMB_colormanagement_space_name_is_srgb(const char *name); /** * Convert a float RGB triplet to the correct luminance weighted average. @@ -71,9 +72,21 @@ BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]); * Byte equivalent of #IMB_colormanagement_get_luminance(). */ BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]); -BLI_INLINE void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]); -BLI_INLINE void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]); -const float *IMB_colormanagement_get_xyz_to_rgb(void); + +/** + * Conversion between scene linear and other color spaces. + */ +BLI_INLINE void IMB_colormanagement_xyz_to_scene_linear(float scene_linear[3], const float xyz[3]); +BLI_INLINE void IMB_colormanagement_scene_linear_to_xyz(float xyz[3], const float scene_linear[3]); +BLI_INLINE void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], + const float rec709[3]); +BLI_INLINE void IMB_colormanagement_scene_linear_to_rec709(float rec709[3], + const float scene_linear[3]); +BLI_INLINE void IMB_colormanagement_aces_to_scene_linear(float scene_linear[3], + const float aces[3]); +BLI_INLINE void IMB_colormanagement_scene_linear_to_aces(float aces[3], + const float scene_linear[3]); +const float *IMB_colormanagement_get_xyz_to_scene_linear(void); /** \} */ @@ -187,15 +200,19 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, * - Color picking values 0..1 map to scene linear values in the 0..1 range, * so that picked albedo values are energy conserving. */ -void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]); -void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]); +void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3], + const float scene_linear[3]); +void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], + const float color_picking[3]); /** * Conversion between sRGB, for rare cases like hex color or copy/pasting * between UI theme and scene linear colors. */ -void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3]); -void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3]); +BLI_INLINE void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], + const float scene_linear[3]); +BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], + const float srgb[3]); /** * Convert pixel from scene linear to display space using default view @@ -497,6 +514,18 @@ enum { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Rendering Tables + * \{ */ + +void IMB_colormanagement_blackbody_temperature_to_rgb_table(float *r_table, + const int width, + const float min, + const float max); +void IMB_colormanagement_wavelength_to_rgb_table(float *r_table, const int width); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 7493fa3e4af..20c414bb1ad 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -936,8 +936,7 @@ const char *IMB_ffmpeg_last_error(void); struct GPUTexture *IMB_create_gpu_texture(const char *name, struct ImBuf *ibuf, bool use_high_bitdepth, - bool use_premult, - bool limit_gl_texture_size); + bool use_premult); /** * The `ibuf` is only here to detect the storage type. The produced texture will have undefined * content. It will need to be populated by using #IMB_update_gpu_texture_sub(). diff --git a/source/blender/imbuf/intern/IMB_colormanagement_intern.h b/source/blender/imbuf/intern/IMB_colormanagement_intern.h index c89b15480a2..23b3f0191b7 100644 --- a/source/blender/imbuf/intern/IMB_colormanagement_intern.h +++ b/source/blender/imbuf/intern/IMB_colormanagement_intern.h @@ -18,8 +18,12 @@ struct ImBuf; struct OCIO_ConstCPUProcessorRcPtr; extern float imbuf_luma_coefficients[3]; -extern float imbuf_xyz_to_rgb[3][3]; -extern float imbuf_rgb_to_xyz[3][3]; +extern float imbuf_scene_linear_to_xyz[3][3]; +extern float imbuf_xyz_to_scene_linear[3][3]; +extern float imbuf_scene_linear_to_aces[3][3]; +extern float imbuf_aces_to_scene_linear[3][3]; +extern float imbuf_scene_linear_to_rec709[3][3]; +extern float imbuf_rec709_to_scene_linear[3][3]; #define MAX_COLORSPACE_NAME 64 #define MAX_COLORSPACE_DESCRIPTION 512 diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 1613595148b..d4c9e78a299 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -73,10 +73,12 @@ static int global_tot_looks = 0; /* Luma coefficients and XYZ to RGB to be initialized by OCIO. */ float imbuf_luma_coefficients[3] = {0.0f}; -float imbuf_xyz_to_rgb[3][3] = {{0.0f}}; -float imbuf_rgb_to_xyz[3][3] = {{0.0f}}; -static float imbuf_xyz_to_linear_srgb[3][3] = {{0.0f}}; -static float imbuf_linear_srgb_to_xyz[3][3] = {{0.0f}}; +float imbuf_scene_linear_to_xyz[3][3] = {{0.0f}}; +float imbuf_xyz_to_scene_linear[3][3] = {{0.0f}}; +float imbuf_scene_linear_to_rec709[3][3] = {{0.0f}}; +float imbuf_rec709_to_scene_linear[3][3] = {{0.0f}}; +float imbuf_scene_linear_to_aces[3][3] = {{0.0f}}; +float imbuf_aces_to_scene_linear[3][3] = {{0.0f}}; /* lock used by pre-cached processors getters, so processor wouldn't * be created several times @@ -573,10 +575,16 @@ static void colormanage_load_config(OCIO_ConstConfigRcPtr *config) /* Load luminance coefficients. */ OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients); - OCIO_configGetXYZtoRGB(config, imbuf_xyz_to_rgb); - invert_m3_m3(imbuf_rgb_to_xyz, imbuf_xyz_to_rgb); - copy_m3_m3(imbuf_xyz_to_linear_srgb, OCIO_XYZ_TO_LINEAR_SRGB); - invert_m3_m3(imbuf_linear_srgb_to_xyz, imbuf_xyz_to_linear_srgb); + + /* Load standard color spaces. */ + OCIO_configGetXYZtoSceneLinear(config, imbuf_xyz_to_scene_linear); + invert_m3_m3(imbuf_scene_linear_to_xyz, imbuf_xyz_to_scene_linear); + + mul_m3_m3m3(imbuf_scene_linear_to_rec709, OCIO_XYZ_TO_REC709, imbuf_scene_linear_to_xyz); + invert_m3_m3(imbuf_rec709_to_scene_linear, imbuf_scene_linear_to_rec709); + + mul_m3_m3m3(imbuf_aces_to_scene_linear, imbuf_xyz_to_scene_linear, OCIO_ACES_TO_XYZ); + invert_m3_m3(imbuf_scene_linear_to_aces, imbuf_aces_to_scene_linear); } static void colormanage_free_config(void) @@ -1412,9 +1420,15 @@ bool IMB_colormanagement_space_name_is_scene_linear(const char *name) return (colorspace && IMB_colormanagement_space_is_scene_linear(colorspace)); } -const float *IMB_colormanagement_get_xyz_to_rgb() +bool IMB_colormanagement_space_name_is_srgb(const char *name) +{ + ColorSpace *colorspace = colormanage_colorspace_get_named(name); + return (colorspace && IMB_colormanagement_space_is_srgb(colorspace)); +} + +const float *IMB_colormanagement_get_xyz_to_scene_linear() { - return &imbuf_xyz_to_rgb[0][0]; + return &imbuf_xyz_to_scene_linear[0][0]; } /** \} */ @@ -2307,7 +2321,8 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, } } -void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]) +void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3], + const float scene_linear[3]) { if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) { /* Create processor if none exists. */ @@ -2329,12 +2344,15 @@ void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]) BLI_mutex_unlock(&processor_lock); } + copy_v3_v3(color_picking, scene_linear); + if (global_color_picking_state.cpu_processor_to) { - OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_to, pixel); + OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_to, color_picking); } } -void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]) +void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], + const float color_picking[3]) { if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) { /* Create processor if none exists. */ @@ -2356,25 +2374,13 @@ void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]) BLI_mutex_unlock(&processor_lock); } + copy_v3_v3(scene_linear, color_picking); + if (global_color_picking_state.cpu_processor_from) { - OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_from, pixel); + OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_from, scene_linear); } } -void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3]) -{ - mul_m3_v3(imbuf_rgb_to_xyz, pixel); - mul_m3_v3(imbuf_xyz_to_linear_srgb, pixel); - linearrgb_to_srgb_v3_v3(pixel, pixel); -} - -void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3]) -{ - srgb_to_linearrgb_v3_v3(pixel, pixel); - mul_m3_v3(imbuf_linear_srgb_to_xyz, pixel); - mul_m3_v3(imbuf_xyz_to_rgb, pixel); -} - void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display) { OCIO_ConstCPUProcessorRcPtr *processor = display_from_scene_linear_processor(display); @@ -4111,3 +4117,170 @@ void IMB_colormanagement_finish_glsl_draw(void) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Rendering Tables + * \{ */ + +/* Calculate color in range 800..12000 using an approximation + * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B + * + * The result of this can be negative to support gamut wider than + * than rec.709, just needs to be clamped. */ + +static const float blackbody_table_r[7][3] = {{1.61919106e+03f, -2.05010916e-03f, 5.02995757e+00f}, + {2.48845471e+03f, -1.11330907e-03f, 3.22621544e+00f}, + {3.34143193e+03f, -4.86551192e-04f, 1.76486769e+00f}, + {4.09461742e+03f, -1.27446582e-04f, 7.25731635e-01f}, + {4.67028036e+03f, 2.91258199e-05f, 1.26703442e-01f}, + {4.59509185e+03f, 2.87495649e-05f, 1.50345020e-01f}, + {3.78717450e+03f, 9.35907826e-06f, 3.99075871e-01f}}; + +static const float blackbody_table_g[7][3] = { + {-4.88999748e+02f, 6.04330754e-04f, -7.55807526e-02f}, + {-7.55994277e+02f, 3.16730098e-04f, 4.78306139e-01f}, + {-1.02363977e+03f, 1.20223470e-04f, 9.36662319e-01f}, + {-1.26571316e+03f, 4.87340896e-06f, 1.27054498e+00f}, + {-1.42529332e+03f, -4.01150431e-05f, 1.43972784e+00f}, + {-1.17554822e+03f, -2.16378048e-05f, 1.30408023e+00f}, + {-5.00799571e+02f, -4.59832026e-06f, 1.09098763e+00f}}; + +static const float blackbody_table_b[7][4] = { + {5.96945309e-11f, -4.85742887e-08f, -9.70622247e-05f, -4.07936148e-03f}, + {2.40430366e-11f, 5.55021075e-08f, -1.98503712e-04f, 2.89312858e-02f}, + {-1.40949732e-11f, 1.89878968e-07f, -3.56632824e-04f, 9.10767778e-02f}, + {-3.61460868e-11f, 2.84822009e-07f, -4.93211319e-04f, 1.56723440e-01f}, + {-1.97075738e-11f, 1.75359352e-07f, -2.50542825e-04f, -2.22783266e-02f}, + {-1.61997957e-13f, -1.64216008e-08f, 3.86216271e-04f, -7.38077418e-01f}, + {6.72650283e-13f, -2.73078809e-08f, 4.24098264e-04f, -7.52335691e-01f}}; + +static void blackbody_temperature_to_rec709(float rec709[3], float t) +{ + if (t >= 12000.0f) { + rec709[0] = 0.8262954810464208f; + rec709[1] = 0.9945080501520986f; + rec709[2] = 1.566307710274283f; + } + else if (t < 800.0f) { + rec709[0] = 5.413294490189271f; + rec709[1] = -0.20319390035873933f; + rec709[2] = -0.0822535242887164f; + } + else { + int i = (t >= 6365.0f) ? 6 : + (t >= 3315.0f) ? 5 : + (t >= 1902.0f) ? 4 : + (t >= 1449.0f) ? 3 : + (t >= 1167.0f) ? 2 : + (t >= 965.0f) ? 1 : + 0; + + const float *r = blackbody_table_r[i]; + const float *g = blackbody_table_g[i]; + const float *b = blackbody_table_b[i]; + + const float t_inv = 1.0f / t; + rec709[0] = r[0] * t_inv + r[1] * t + r[2]; + rec709[1] = g[0] * t_inv + g[1] * t + g[2]; + rec709[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3]; + } +} + +void IMB_colormanagement_blackbody_temperature_to_rgb_table(float *r_table, + const int width, + const float min, + const float max) +{ + for (int i = 0; i < width; i++) { + float temperature = min + (max - min) / (float)width * (float)i; + + float rec709[3]; + blackbody_temperature_to_rec709(rec709, temperature); + + float rgb[3]; + IMB_colormanagement_rec709_to_scene_linear(rgb, rec709); + clamp_v3(rgb, 0.0f, FLT_MAX); + + copy_v3_v3(&r_table[i * 4], rgb); + r_table[i * 4 + 3] = 0.0f; + } +} + +/** + * CIE color matching functions `xBar`, `yBar`, and `zBar` for + * wavelengths from 380 through 780 nanometers, every 5 nanometers. + * + * For a wavelength lambda in this range: + * \code{.txt} + * cie_color_match[(lambda - 380) / 5][0] = xBar + * cie_color_match[(lambda - 380) / 5][1] = yBar + * cie_color_match[(lambda - 380) / 5][2] = zBar + * \endcode + */ + +static float cie_colour_match[81][3] = { + {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, + {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, + {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, + {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, + {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, + {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, + {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, + {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, + {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, + {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, + {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, + {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, + {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, + {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, + {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, + {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, + {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, + {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, + {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, + {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, + {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, + {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, + {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, + {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, + {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, + {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, + {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; + +static void wavelength_to_xyz(float xyz[3], float lambda_nm) +{ + float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */ + int i = (int)ii; + + if (i < 0 || i >= 80) { + xyz[0] = 0.0f; + xyz[1] = 0.0f; + xyz[2] = 0.0f; + } + else { + ii -= (float)i; + const float *c = cie_colour_match[i]; + xyz[0] = c[0] + ii * (c[3] - c[0]); + xyz[1] = c[1] + ii * (c[4] - c[1]); + xyz[2] = c[2] + ii * (c[5] - c[2]); + } +} + +void IMB_colormanagement_wavelength_to_rgb_table(float *r_table, const int width) +{ + for (int i = 0; i < width; i++) { + float temperature = 380 + 400 / (float)width * (float)i; + + float xyz[3]; + wavelength_to_xyz(xyz, temperature); + + float rgb[3]; + IMB_colormanagement_xyz_to_scene_linear(rgb, xyz); + clamp_v3(rgb, 0.0f, FLT_MAX); + + copy_v3_v3(&r_table[i * 4], rgb); + r_table[i * 4 + 3] = 0.0f; + } +} + +/** \} */ diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c index 411cf9af802..668307ec802 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.c +++ b/source/blender/imbuf/intern/colormanagement_inline.c @@ -27,14 +27,46 @@ unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3]) return unit_float_to_uchar_clamp(val); } -void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]) +void IMB_colormanagement_xyz_to_scene_linear(float scene_linear[3], const float xyz[3]) { - mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, xyz); + mul_v3_m3v3(scene_linear, imbuf_xyz_to_scene_linear, xyz); } -void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]) +void IMB_colormanagement_scene_linear_to_xyz(float xyz[3], const float scene_linear[3]) { - mul_v3_m3v3(xyz, imbuf_rgb_to_xyz, rgb); + mul_v3_m3v3(xyz, imbuf_scene_linear_to_xyz, scene_linear); +} + +void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], const float rec709[3]) +{ + mul_v3_m3v3(scene_linear, imbuf_rec709_to_scene_linear, rec709); +} + +void IMB_colormanagement_scene_linear_to_rec709(float rec709[3], const float scene_linear[3]) +{ + mul_v3_m3v3(rec709, imbuf_scene_linear_to_rec709, scene_linear); +} + +void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3]) +{ + mul_v3_m3v3(srgb, imbuf_scene_linear_to_rec709, scene_linear); + linearrgb_to_srgb_v3_v3(srgb, srgb); +} + +void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3]) +{ + srgb_to_linearrgb_v3_v3(scene_linear, srgb); + mul_m3_v3(imbuf_rec709_to_scene_linear, scene_linear); +} + +void IMB_colormanagement_aces_to_scene_linear(float scene_linear[3], const float aces[3]) +{ + mul_v3_m3v3(scene_linear, imbuf_aces_to_scene_linear, aces); +} + +void IMB_colormanagement_scene_linear_to_aces(float aces[3], const float scene_linear[3]) +{ + mul_v3_m3v3(aces, imbuf_scene_linear_to_aces, scene_linear); } #endif /* __IMB_COLORMANAGEMENT_INLINE_H__ */ diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 8da9eb9ccf7..8e004938a89 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -197,12 +197,10 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex, GPUTexture *IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, - bool use_premult, - bool limit_gl_texture_size) + bool use_premult) { GPUTexture *tex = NULL; - int size[2] = {GPU_texture_size_with_limit(ibuf->x, limit_gl_texture_size), - GPU_texture_size_with_limit(ibuf->y, limit_gl_texture_size)}; + int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); #ifdef WITH_DDS diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 7a7c95b29f9..1205ae74e6f 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -43,6 +43,7 @@ set(INC ../../bmesh ../../depsgraph ../../editors/include + ../../imbuf ../../makesdna ../../makesrna ../../windowmanager @@ -114,6 +115,7 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + bf_imbuf bf_io_common ) diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index a24877a20bd..857896b9330 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -10,6 +10,8 @@ #include "BKE_main.h" #include "BKE_node.h" +#include "IMB_colormanagement.h" + #include "BLI_fileops.h" #include "BLI_linklist.h" #include "BLI_listbase.h" @@ -414,13 +416,10 @@ static pxr::TfToken get_node_tex_image_color_space(bNode *node) Image *ima = reinterpret_cast<Image *>(node->id); - if (strcmp(ima->colorspace_settings.name, "Raw") == 0) { - return usdtokens::raw; - } - if (strcmp(ima->colorspace_settings.name, "Non-Color") == 0) { + if (IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) { return usdtokens::raw; } - if (strcmp(ima->colorspace_settings.name, "sRGB") == 0) { + if (IMB_colormanagement_space_name_is_srgb(ima->colorspace_settings.name)) { return usdtokens::sRGB; } diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index f7bf678310f..bebad06d37f 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -92,12 +92,14 @@ struct OBJImportParams { }; /** - * Time the full import process. + * Perform the full import process. + * Import also changes the selection & the active object; callers + * need to update the UI bits if needed. */ void OBJ_import(bContext *C, const struct OBJImportParams *import_params); /** - * C-interface for the exporter. + * Perform the full export process. */ void OBJ_export(bContext *C, const struct OBJExportParams *export_params); diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index b18ff2cf454..f2051d195c8 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -88,7 +88,6 @@ void importer_main(bContext *C, const OBJImportParams &import_params) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); importer_main(bmain, scene, view_layer, import_params); - static_cast<void>(CTX_data_ensure_evaluated_depsgraph(C)); } void importer_main(Main *bmain, diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 535533565dd..c20fb180fcd 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -229,6 +229,7 @@ typedef enum eTimeGpencil_Mode { GP_TIME_MODE_NORMAL = 0, GP_TIME_MODE_REVERSE = 1, GP_TIME_MODE_FIX = 2, + GP_TIME_MODE_PINGPONG = 3, } eTimeGpencil_Mode; typedef enum eModifyColorGpencil_Flag { diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 80f592bb66d..6e4e515a0fe 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -80,17 +80,11 @@ typedef struct RenderSlot { struct RenderResult *render; } RenderSlot; -typedef struct ImageTile_RuntimeTextureSlot { +typedef struct ImageTile_Runtime { int tilearray_layer; int _pad; int tilearray_offset[2]; int tilearray_size[2]; -} ImageTile_RuntimeTextureSlot; - -typedef struct ImageTile_Runtime { - /* Data per `eImageTextureResolution`. - * Should match `IMA_TEXTURE_RESOLUTION_LEN` */ - ImageTile_RuntimeTextureSlot slots[2]; } ImageTile_Runtime; typedef struct ImageTile { @@ -109,10 +103,7 @@ typedef struct ImageTile { /* #define IMA_UNUSED_2 (1 << 2) */ #define IMA_NEED_FRAME_RECALC (1 << 3) #define IMA_SHOW_STEREO (1 << 4) -/* Do not limit the resolution by the limit texture size option in the user preferences. - * Images in the image editor or used as a backdrop are always shown using the maximum - * possible resolution. */ -#define IMA_SHOW_MAX_RESOLUTION (1 << 5) +/* #define IMA_UNUSED_5 (1 << 5) */ /* Used to get the correct gpu texture from an Image datablock. */ typedef enum eGPUTextureTarget { @@ -122,15 +113,6 @@ typedef enum eGPUTextureTarget { TEXTARGET_COUNT, } eGPUTextureTarget; -/* Resolution variations that can be cached for an image. */ -typedef enum eImageTextureResolution { - IMA_TEXTURE_RESOLUTION_FULL = 0, - IMA_TEXTURE_RESOLUTION_LIMITED, - - /* Not an option, but holds the number of options defined for this struct. */ - IMA_TEXTURE_RESOLUTION_LEN -} eImageTextureResolution; - /* Defined in BKE_image.h. */ struct PartialUpdateRegister; struct PartialUpdateUser; @@ -155,8 +137,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes, 2 = IMA_TEXTURE_RESOLUTION_LEN. */ - struct GPUTexture *gputexture[3][2][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ + struct GPUTexture *gputexture[3][2]; /* sources from: */ ListBase anims; @@ -244,11 +226,6 @@ enum { enum { /** All mipmap levels in OpenGL texture set? */ IMA_GPU_MIPMAP_COMPLETE = (1 << 0), - /* Reuse the max resolution textures as they fit in the limited scale. */ - IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 1), - /* Has any limited scale textures been allocated. - * Adds additional checks to reuse max resolution images when they fit inside limited scale. */ - IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 2), }; /* Image.source, where the image comes from */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index e9c12e52bce..3be4f82ecb0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -610,6 +610,8 @@ typedef enum eNodeTreeExecutionMode { typedef enum eNodeTreeRuntimeFlag { /** There is a node that references an image with animation. */ NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION = 1 << 0, + /** There is a material output node in the group. */ + NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT = 1 << 1, } eNodeTreeRuntimeFlag; /* socket value structs for input buttons diff --git a/source/blender/makesdna/DNA_volume_defaults.h b/source/blender/makesdna/DNA_volume_defaults.h index ee98f0ea4fd..1057fc75d34 100644 --- a/source/blender/makesdna/DNA_volume_defaults.h +++ b/source/blender/makesdna/DNA_volume_defaults.h @@ -23,6 +23,7 @@ #define _DNA_DEFAULT_VolumeRender \ { \ + .precision = VOLUME_PRECISION_HALF, \ .space = VOLUME_SPACE_OBJECT, \ .step_size = 0.0f, \ .clipping = 0.001f, \ diff --git a/source/blender/makesdna/DNA_volume_types.h b/source/blender/makesdna/DNA_volume_types.h index a2e558aa790..a25bfe0ebec 100644 --- a/source/blender/makesdna/DNA_volume_types.h +++ b/source/blender/makesdna/DNA_volume_types.h @@ -126,6 +126,13 @@ typedef enum VolumeWireframeDetail { VOLUME_WIREFRAME_FINE = 1, } VolumeWireframeDetail; +/** #VolumeRender.precision */ +typedef enum VolumeRenderPrecision { + VOLUME_PRECISION_HALF = 0, + VOLUME_PRECISION_FULL = 1, + VOLUME_PRECISION_VARIABLE = 2, +} VolumeRenderPrecision; + /** #VolumeRender.space */ typedef enum VolumeRenderSpace { VOLUME_SPACE_OBJECT = 0, diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index e1e655fad4b..4267ce47d81 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -177,7 +177,7 @@ int RNA_property_multi_array_length(PointerRNA *ptr, PropertyRNA *prop, int dime /** * Used by BPY to make an array from the python object. */ -int RNA_property_array_dimension(PointerRNA *ptr, PropertyRNA *prop, int length[]); +int RNA_property_array_dimension(const PointerRNA *ptr, PropertyRNA *prop, int length[]); char RNA_property_array_item_char(PropertyRNA *prop, int index); int RNA_property_array_item_index(PropertyRNA *prop, char name); @@ -358,6 +358,21 @@ char *RNA_property_string_get_alloc( PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len); void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value); void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len); + +eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop); +/** + * Search candidates for string `prop` by calling `visit_fn` with each string. + * Typically these strings are collected in `visit_user_data` in a format defined by the caller. + * + * See #PropStringSearchFunc for details. + */ +void RNA_property_string_search(const struct bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /** * \return the length without `\0` terminator. */ @@ -402,7 +417,9 @@ int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop); * without having to iterate over items in the collection (needed for some kinds of collections). */ bool RNA_property_collection_is_empty(PointerRNA *ptr, PropertyRNA *prop); -int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *t_ptr); +int RNA_property_collection_lookup_index(PointerRNA *ptr, + PropertyRNA *prop, + const PointerRNA *t_ptr); int RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, @@ -468,7 +485,7 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop); * UI code or Actions, though efficiency is a concern. */ char *RNA_path_append( - const char *path, PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); + const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); #if 0 /* UNUSED. */ char *RNA_path_back(const char *path); #endif @@ -486,7 +503,10 @@ char *RNA_path_back(const char *path); * \note Assumes all pointers provided are valid * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" */ -bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop); +bool RNA_path_resolve(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop); /** * Resolve the given RNA Path to find the pointer and/or property + array index @@ -495,16 +515,22 @@ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, Prop * \note Assumes all pointers provided are valid. * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" */ -bool RNA_path_resolve_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); /** * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data. * * \note While it's correct to ignore the value of #PointerRNA.data * most callers need to know if the resulting pointer was found and not null. */ -bool RNA_path_resolve_full_maybe_null( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); /* RNA_path_resolve_property() variants ensure that pointer + property both exist. */ @@ -516,7 +542,7 @@ bool RNA_path_resolve_full_maybe_null( * \note Assumes all pointers provided are valid * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property(PointerRNA *ptr, +bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop); @@ -529,8 +555,11 @@ bool RNA_path_resolve_property(PointerRNA *ptr, * \note Assumes all pointers provided are valid * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_property_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); /* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist, * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */ @@ -547,7 +576,7 @@ bool RNA_path_resolve_property_full( * You must check for its validity before use! * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -566,7 +595,7 @@ bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, * You must check for its validity before use! * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property_and_item_pointer_full(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -614,21 +643,23 @@ struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const ch char *RNA_path_from_ID_to_struct(const PointerRNA *ptr); -char *RNA_path_from_real_ID_to_struct(struct Main *bmain, PointerRNA *ptr, struct ID **r_real); +char *RNA_path_from_real_ID_to_struct(struct Main *bmain, + const PointerRNA *ptr, + struct ID **r_real); -char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop); +char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop); /** * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc. * \param index: The *flattened* index to use when \a `index_dim > 0`, * this is expanded when used with multi-dimensional arrays. */ -char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, +char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index); char *RNA_path_from_real_ID_to_property_index(struct Main *bmain, - PointerRNA *ptr, + const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, @@ -638,8 +669,8 @@ char *RNA_path_from_real_ID_to_property_index(struct Main *bmain, * \return the path to given ptr/prop from the closest ancestor of given type, * if any (else return NULL). */ -char *RNA_path_resolve_from_type_to_property(struct PointerRNA *ptr, - struct PropertyRNA *prop, +char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, + PropertyRNA *prop, const struct StructRNA *type); /** @@ -651,27 +682,27 @@ char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id); * Get the ID.struct as a python representation, eg: * bpy.data.foo["bar"].some_struct */ -char *RNA_path_full_struct_py(struct Main *bmain, struct PointerRNA *ptr); +char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr); /** * Get the ID.struct.property as a python representation, eg: * bpy.data.foo["bar"].some_struct.some_prop[10] */ char *RNA_path_full_property_py_ex( - struct Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback); + struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback); char *RNA_path_full_property_py(struct Main *bmain, - struct PointerRNA *ptr, - struct PropertyRNA *prop, + const PointerRNA *ptr, + PropertyRNA *prop, int index); /** * Get the struct.property as a python representation, eg: * some_struct.some_prop[10] */ -char *RNA_path_struct_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index); /** * Get the struct.property as a python representation, eg: * some_prop[10] */ -char *RNA_path_property_py(const struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index); /* Quick name based property access * diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 13a5ec66a16..0389d1b3b16 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -446,6 +446,9 @@ void RNA_def_property_string_funcs(PropertyRNA *prop, const char *get, const char *length, const char *set); +void RNA_def_property_string_search_func(PropertyRNA *prop, + const char *search, + eStringPropertySearchFlag search_flag); void RNA_def_property_pointer_funcs( PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll); void RNA_def_property_collection_funcs(PropertyRNA *prop, @@ -490,6 +493,9 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop, StringPropertyGetFunc getfunc, StringPropertyLengthFunc lengthfunc, StringPropertySetFunc setfunc); +void RNA_def_property_string_search_func_runtime(PropertyRNA *prop, + StringPropertySearchFunc search_fn, + eStringPropertySearchFlag search_flag); void RNA_def_property_translation_context(PropertyRNA *prop, const char *context); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 3ebcae5f947..75b514cdb13 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -515,6 +515,55 @@ typedef int (*StringPropertyLengthFunc)(struct PointerRNA *ptr, struct PropertyR typedef void (*StringPropertySetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop, const char *value); + +typedef struct StringPropertySearchVisitParams { + /** Text being searched for (never NULL). */ + const char *text; + /** Additional information to display (optional, may be NULL). */ + const char *info; +} StringPropertySearchVisitParams; + +typedef enum eStringPropertySearchFlag { + /** + * Used so the result of #RNA_property_string_search_flag can be used to check + * if search is supported. + */ + PROP_STRING_SEARCH_SUPPORTED = (1 << 0), + /** Items resulting from the search must be sorted. */ + PROP_STRING_SEARCH_SORT = (1 << 1), + /** + * Allow members besides the ones listed to be entered. + * + * \warning disabling this options causes the search callback to run on redraw and should + * only be enabled this doesn't cause performance issues. + */ + PROP_STRING_SEARCH_SUGGESTION = (1 << 2), +} eStringPropertySearchFlag; + +/** + * Visit string search candidates, `text` may be freed once this callback has finished, + * so references to it should not be held. + */ +typedef void (*StringPropertySearchVisitFunc)(void *visit_user_data, + const StringPropertySearchVisitParams *params); +/** + * \param C: context, may be NULL (in this case all available items should be shown). + * \param ptr: RNA pointer. + * \param prop: RNA property. This must have it's #StringPropertyRNA.search callback set, + * to check this use `RNA_property_string_search_flag(prop) & PROP_STRING_SEARCH_SUPPORTED`. + * \param edit_text: Optionally use the string being edited by the user as a basis + * for the search results (auto-complete Python attributes for e.g.). + * \param visit_fn: This function is called with every search candidate and is typically + * responsible for storing the search results. + * \param visit_user_data: Caller defined data, passed to `visit_fn`. + */ +typedef void (*StringPropertySearchFunc)(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + typedef int (*EnumPropertyGetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop); typedef void (*EnumPropertySetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop, int value); /* same as PropEnumItemFunc */ diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index c3ca57b38bf..400944d60d4 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1039,6 +1039,38 @@ static void rna_clamp_value(FILE *f, PropertyRNA *prop, int array) } } +static char *rna_def_property_search_func(FILE *f, + StructRNA *srna, + PropertyRNA *prop, + PropertyDefRNA *UNUSED(dp), + const char *manualfunc) +{ + char *func; + + if (prop->flag & PROP_IDPROPERTY && manualfunc == NULL) { + return NULL; + } + if (!manualfunc) { + return NULL; + } + + func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "search"); + + fprintf(f, + "void %s(" + "const bContext *C, " + "PointerRNA *ptr, " + "PropertyRNA *prop, " + "const char *edit_text, " + "StringPropertySearchVisitFunc visit_fn, " + "void *visit_user_data)\n", + func); + fprintf(f, "{\n"); + fprintf(f, "\n %s(C, ptr, prop, edit_text, visit_fn, visit_user_data);\n", manualfunc); + fprintf(f, "}\n\n"); + return func; +} + static char *rna_def_property_set_func( FILE *f, StructRNA *srna, PropertyRNA *prop, PropertyDefRNA *dp, const char *manualfunc) { @@ -1895,6 +1927,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp) sprop->length = (void *)rna_def_property_length_func( f, srna, prop, dp, (const char *)sprop->length); sprop->set = (void *)rna_def_property_set_func(f, srna, prop, dp, (const char *)sprop->set); + sprop->search = (void *)rna_def_property_search_func( + f, srna, prop, dp, (const char *)sprop->search); break; } case PROP_POINTER: { @@ -4081,13 +4115,15 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr case PROP_STRING: { StringPropertyRNA *sprop = (StringPropertyRNA *)prop; fprintf(f, - "\t%s, %s, %s, %s, %s, %s, %d, ", + "\t%s, %s, %s, %s, %s, %s, %s, %d, %d, ", rna_function_string(sprop->get), rna_function_string(sprop->length), rna_function_string(sprop->set), rna_function_string(sprop->get_ex), rna_function_string(sprop->length_ex), rna_function_string(sprop->set_ex), + rna_function_string(sprop->search), + (int)sprop->search_flag, sprop->maxlength); rna_print_c_string(f, sprop->defaultvalue); fprintf(f, "\n"); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b0488bbfa7a..b5cf8abaac6 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -722,7 +722,7 @@ static ID *rna_ID_override_hierarchy_create( ID *id_root_override = NULL; BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override); + bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override, false); WM_main_add_notifier(NC_ID | NA_ADDED, NULL); WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); @@ -1131,7 +1131,7 @@ static void rna_ImagePreview_size_set(PointerRNA *ptr, const int *values, enum e prv_img->flag[size] |= (PRV_CHANGED | PRV_USER_EDITED); } -static int rna_ImagePreview_pixels_get_length(PointerRNA *ptr, +static int rna_ImagePreview_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION], enum eIconSizes size) { @@ -1176,7 +1176,7 @@ static void rna_ImagePreview_pixels_set(PointerRNA *ptr, const int *values, enum prv_img->flag[size] |= PRV_USER_EDITED; } -static int rna_ImagePreview_pixels_float_get_length(PointerRNA *ptr, +static int rna_ImagePreview_pixels_float_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION], enum eIconSizes size) { @@ -1256,7 +1256,7 @@ static void rna_ImagePreview_image_size_set(PointerRNA *ptr, const int *values) rna_ImagePreview_size_set(ptr, values, ICON_SIZE_PREVIEW); } -static int rna_ImagePreview_image_pixels_get_length(PointerRNA *ptr, +static int rna_ImagePreview_image_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_get_length(ptr, length, ICON_SIZE_PREVIEW); @@ -1272,7 +1272,7 @@ static void rna_ImagePreview_image_pixels_set(PointerRNA *ptr, const int *values rna_ImagePreview_pixels_set(ptr, values, ICON_SIZE_PREVIEW); } -static int rna_ImagePreview_image_pixels_float_get_length(PointerRNA *ptr, +static int rna_ImagePreview_image_pixels_float_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_float_get_length(ptr, length, ICON_SIZE_PREVIEW); @@ -1303,7 +1303,7 @@ static void rna_ImagePreview_icon_size_set(PointerRNA *ptr, const int *values) rna_ImagePreview_size_set(ptr, values, ICON_SIZE_ICON); } -static int rna_ImagePreview_icon_pixels_get_length(PointerRNA *ptr, +static int rna_ImagePreview_icon_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_get_length(ptr, length, ICON_SIZE_ICON); @@ -1319,7 +1319,7 @@ static void rna_ImagePreview_icon_pixels_set(PointerRNA *ptr, const int *values) rna_ImagePreview_pixels_set(ptr, values, ICON_SIZE_ICON); } -static int rna_ImagePreview_icon_pixels_float_get_length(PointerRNA *ptr, +static int rna_ImagePreview_icon_pixels_float_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_float_get_length(ptr, length, ICON_SIZE_ICON); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 53fa7a3d923..c8cb0b7ffb8 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -302,7 +302,8 @@ static int rna_ensure_property_array_length(PointerRNA *ptr, PropertyRNA *prop) { if (prop->magic == RNA_MAGIC) { int arraylen[RNA_MAX_ARRAY_DIMENSION]; - return (prop->getlength && ptr->data) ? prop->getlength(ptr, arraylen) : prop->totarraylength; + return (prop->getlength && ptr->data) ? prop->getlength(ptr, arraylen) : + (int)prop->totarraylength; } IDProperty *idprop = (IDProperty *)prop; @@ -322,7 +323,7 @@ static bool rna_ensure_property_array_check(PropertyRNA *prop) return (idprop->type == IDP_ARRAY); } -static void rna_ensure_property_multi_array_length(PointerRNA *ptr, +static void rna_ensure_property_multi_array_length(const PointerRNA *ptr, PropertyRNA *prop, int length[]) { @@ -1080,7 +1081,7 @@ bool RNA_property_array_check(PropertyRNA *prop) return rna_ensure_property_array_check(prop); } -int RNA_property_array_dimension(PointerRNA *ptr, PropertyRNA *prop, int length[]) +int RNA_property_array_dimension(const PointerRNA *ptr, PropertyRNA *prop, int length[]) { PropertyRNA *rprop = rna_ensure_property(prop); @@ -3368,6 +3369,34 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro return strlen(sprop->defaultvalue); } +eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop) +{ + StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); + if (prop->magic != RNA_MAGIC) { + return false; + } + BLI_assert(RNA_property_type(prop) == PROP_STRING); + if (sprop->search) { + BLI_assert(sprop->search_flag & PROP_STRING_SEARCH_SUPPORTED); + } + else { + BLI_assert(sprop->search_flag == 0); + } + return sprop->search_flag; +} + +void RNA_property_string_search(const bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + BLI_assert(RNA_property_string_search_flag(prop) & PROP_STRING_SEARCH_SUPPORTED); + StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); + sprop->search(C, ptr, prop, edit_text, visit_fn, visit_user_data); +} + int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop) { EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop; @@ -4025,7 +4054,9 @@ void RNA_property_collection_clear(PointerRNA *ptr, PropertyRNA *prop) } } -int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *t_ptr) +int RNA_property_collection_lookup_index(PointerRNA *ptr, + PropertyRNA *prop, + const PointerRNA *t_ptr) { CollectionPropertyIterator iter; int index = 0; @@ -5105,7 +5136,7 @@ static bool rna_path_parse_array_index(const char **path, * * \return \a true on success, \a false if the path is somehow invalid. */ -static bool rna_path_parse(PointerRNA *ptr, +static bool rna_path_parse(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -5262,7 +5293,10 @@ static bool rna_path_parse(PointerRNA *ptr, return true; } -bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop) +bool RNA_path_resolve(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop) { if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) { return false; @@ -5272,7 +5306,7 @@ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, Prop } bool RNA_path_resolve_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) { if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) { return false; @@ -5282,12 +5316,12 @@ bool RNA_path_resolve_full( } bool RNA_path_resolve_full_maybe_null( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) { return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true); } -bool RNA_path_resolve_property(PointerRNA *ptr, +bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop) @@ -5300,7 +5334,7 @@ bool RNA_path_resolve_property(PointerRNA *ptr, } bool RNA_path_resolve_property_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) { if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) { return false; @@ -5309,7 +5343,7 @@ bool RNA_path_resolve_property_full( return r_ptr->data != NULL && *r_prop != NULL; } -bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -5322,7 +5356,7 @@ bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, return r_ptr->data != NULL && *r_prop != NULL; } -bool RNA_path_resolve_property_and_item_pointer_full(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -5340,8 +5374,11 @@ bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_el return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false); } -char *RNA_path_append( - const char *path, PointerRNA *UNUSED(ptr), PropertyRNA *prop, int intkey, const char *strkey) +char *RNA_path_append(const char *path, + const PointerRNA *UNUSED(ptr), + PropertyRNA *prop, + int intkey, + const char *strkey) { DynStr *dynstr; char *result; @@ -5724,7 +5761,7 @@ char *RNA_path_from_ID_to_struct(const PointerRNA *ptr) return ptrpath; } -char *RNA_path_from_real_ID_to_struct(Main *bmain, PointerRNA *ptr, struct ID **r_real) +char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real) { char *path = RNA_path_from_ID_to_struct(ptr); @@ -5753,7 +5790,7 @@ static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY BLI_assert(index == 0); } -static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr, +static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, @@ -5772,7 +5809,7 @@ static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr, } } -char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, +char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index) @@ -5828,13 +5865,17 @@ char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, return path; } -char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) +char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop) { return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1); } -char *RNA_path_from_real_ID_to_property_index( - Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, ID **r_real_id) +char *RNA_path_from_real_ID_to_property_index(Main *bmain, + const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index, + ID **r_real_id) { char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index); @@ -5843,7 +5884,7 @@ char *RNA_path_from_real_ID_to_property_index( return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL; } -char *RNA_path_resolve_from_type_to_property(PointerRNA *ptr, +char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, PropertyRNA *prop, const StructRNA *type) { @@ -5916,7 +5957,7 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id) path); } -char *RNA_path_full_struct_py(Main *bmain, struct PointerRNA *ptr) +char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr) { char *id_path; char *data_path; @@ -5945,7 +5986,7 @@ char *RNA_path_full_struct_py(Main *bmain, struct PointerRNA *ptr) } char *RNA_path_full_property_py_ex( - Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback) + Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback) { char *id_path; const char *data_delim; @@ -5992,7 +6033,7 @@ char *RNA_path_full_property_py_ex( return ret; } -char *RNA_path_full_property_py(Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index) +char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index) { return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false); } diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index 76d2087d904..a1266443631 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -340,7 +340,7 @@ bool rna_Action_actedit_assign_poll(PointerRNA *ptr, PointerRNA value) return 0; } -static char *rna_DopeSheet_path(PointerRNA *UNUSED(ptr)) +static char *rna_DopeSheet_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("dopesheet"); } diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index df92601dd0c..a4094630266 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -216,10 +216,10 @@ static void rna_Bone_select_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN, id); } -static char *rna_Bone_path(PointerRNA *ptr) +static char *rna_Bone_path(const PointerRNA *ptr) { - ID *id = ptr->owner_id; - Bone *bone = (Bone *)ptr->data; + const ID *id = ptr->owner_id; + const Bone *bone = (const Bone *)ptr->data; char name_esc[sizeof(bone->name) * 2]; BLI_str_escape(name_esc, bone->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 2d9517ce1ee..36706c82366 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -118,9 +118,9 @@ const EnumPropertyItem rna_enum_color_attribute_domain_items[] = { /* Attribute */ -static char *rna_Attribute_path(PointerRNA *ptr) +static char *rna_Attribute_path(const PointerRNA *ptr) { - CustomDataLayer *layer = ptr->data; + const CustomDataLayer *layer = ptr->data; return BLI_sprintfN("attributes['%s']", layer->name); } diff --git a/source/blender/makesrna/intern/rna_boid.c b/source/blender/makesrna/intern/rna_boid.c index 0818f009d1f..d65c8a1b4e3 100644 --- a/source/blender/makesrna/intern/rna_boid.c +++ b/source/blender/makesrna/intern/rna_boid.c @@ -162,9 +162,9 @@ static StructRNA *rna_BoidRule_refine(struct PointerRNA *ptr) } } -static char *rna_BoidRule_path(PointerRNA *ptr) +static char *rna_BoidRule_path(const PointerRNA *ptr) { - BoidRule *rule = (BoidRule *)ptr->data; + const BoidRule *rule = (BoidRule *)ptr->data; char name_esc[sizeof(rule->name) * 2]; BLI_str_escape(name_esc, rule->name, sizeof(name_esc)); @@ -222,16 +222,16 @@ static void rna_BoidState_active_boid_rule_index_set(struct PointerRNA *ptr, int } } -static int particle_id_check(PointerRNA *ptr) +static int particle_id_check(const PointerRNA *ptr) { - ID *id = ptr->owner_id; + const ID *id = ptr->owner_id; return (GS(id->name) == ID_PA); } -static char *rna_BoidSettings_path(PointerRNA *ptr) +static char *rna_BoidSettings_path(const PointerRNA *ptr) { - BoidSettings *boids = (BoidSettings *)ptr->data; + const BoidSettings *boids = (BoidSettings *)ptr->data; if (particle_id_check(ptr)) { ParticleSettings *part = (ParticleSettings *)ptr->owner_id; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 4767ef2c017..72f5bd1923c 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -934,7 +934,7 @@ static const EnumPropertyItem *rna_Brush_stroke_itemf(bContext *C, } /* Grease Pencil Drawing Brushes Settings */ -static char *rna_BrushGpencilSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_BrushGpencilSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_paint.brush.gpencil_settings"); } diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index f535cdcee96..92df7293c2e 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -117,7 +117,7 @@ static void rna_Camera_dof_update(Main *bmain, Scene *scene, PointerRNA *UNUSED( WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } -char *rna_CameraDOFSettings_path(PointerRNA *ptr) +char *rna_CameraDOFSettings_path(const PointerRNA *ptr) { /* if there is ID-data, resolve the path using the index instead of by name, * since the name used is the name of the texture assigned, but the texture diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index 3ad901e5397..e45a1a3cc33 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -436,10 +436,10 @@ static void rna_ClothSettings_gravity_set(PointerRNA *ptr, const float *values) sim->gravity[2] = values[2]; } -static char *rna_ClothSettings_path(PointerRNA *ptr) +static char *rna_ClothSettings_path(const PointerRNA *ptr) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); + const Object *ob = (Object *)ptr->owner_id; + const ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); if (md) { char name_esc[sizeof(md->name) * 2]; @@ -451,10 +451,10 @@ static char *rna_ClothSettings_path(PointerRNA *ptr) } } -static char *rna_ClothCollisionSettings_path(PointerRNA *ptr) +static char *rna_ClothCollisionSettings_path(const PointerRNA *ptr) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); + const Object *ob = (Object *)ptr->owner_id; + const ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); if (md) { char name_esc[sizeof(md->name) * 2]; diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 840674c7bc6..92cdcc6d781 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -157,7 +157,7 @@ static void rna_CurveMapping_clipmaxy_range( *max = 100.0f; } -static char *rna_ColorRamp_path(PointerRNA *ptr) +static char *rna_ColorRamp_path(const PointerRNA *ptr) { char *path = NULL; @@ -208,7 +208,7 @@ static char *rna_ColorRamp_path(PointerRNA *ptr) return path; } -static char *rna_ColorRampElement_path(PointerRNA *ptr) +static char *rna_ColorRampElement_path(const PointerRNA *ptr) { PointerRNA ramp_ptr; PropertyRNA *prop; @@ -438,7 +438,7 @@ static void rna_ColorManagedDisplaySettings_display_device_update(Main *bmain, } } -static char *rna_ColorManagedDisplaySettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedDisplaySettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("display_settings"); } @@ -526,7 +526,7 @@ static void rna_ColorManagedViewSettings_use_curves_set(PointerRNA *ptr, bool va } } -static char *rna_ColorManagedViewSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedViewSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("view_settings"); } @@ -662,12 +662,12 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, } } -static char *rna_ColorManagedSequencerColorspaceSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedSequencerColorspaceSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("sequencer_colorspace_settings"); } -static char *rna_ColorManagedInputColorspaceSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedInputColorspaceSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("colorspace_settings"); } diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 63d8876ec8b..8829c655030 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -440,7 +440,7 @@ static char *rna_Constraint_do_compute_path(Object *ob, bConstraint *con) } } -static char *rna_Constraint_path(PointerRNA *ptr) +static char *rna_Constraint_path(const PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; bConstraint *con = ptr->data; @@ -448,7 +448,7 @@ static char *rna_Constraint_path(PointerRNA *ptr) return rna_Constraint_do_compute_path(ob, con); } -static bConstraint *rna_constraint_from_target(PointerRNA *ptr) +static bConstraint *rna_constraint_from_target(const PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; bConstraintTarget *tgt = ptr->data; @@ -456,7 +456,7 @@ static bConstraint *rna_constraint_from_target(PointerRNA *ptr) return BKE_constraint_find_from_target(ob, tgt, NULL); } -static char *rna_ConstraintTarget_path(PointerRNA *ptr) +static char *rna_ConstraintTarget_path(const PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; bConstraintTarget *tgt = ptr->data; @@ -691,11 +691,11 @@ static void rna_ActionConstraint_minmax_range( } } -static int rna_SplineIKConstraint_joint_bindings_get_length(PointerRNA *ptr, +static int rna_SplineIKConstraint_joint_bindings_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - bConstraint *con = (bConstraint *)ptr->data; - bSplineIKConstraint *ikData = (bSplineIKConstraint *)con->data; + const bConstraint *con = (bConstraint *)ptr->data; + const bSplineIKConstraint *ikData = (bSplineIKConstraint *)con->data; if (ikData) { length[0] = ikData->numpoints; diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index fb911725836..49b78e90024 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -770,7 +770,7 @@ static void rna_Curve_active_spline_set(PointerRNA *ptr, } } -static char *rna_Curve_spline_path(PointerRNA *ptr) +static char *rna_Curve_spline_path(const PointerRNA *ptr) { Curve *cu = (Curve *)ptr->owner_id; ListBase *nubase = BKE_curve_nurbs_get(cu); @@ -786,7 +786,7 @@ static char *rna_Curve_spline_path(PointerRNA *ptr) } /* use for both bezier and nurbs */ -static char *rna_Curve_spline_point_path(PointerRNA *ptr) +static char *rna_Curve_spline_point_path(const PointerRNA *ptr) { Curve *cu = (Curve *)ptr->owner_id; Nurb *nu; @@ -808,10 +808,10 @@ static char *rna_Curve_spline_point_path(PointerRNA *ptr) } } -static char *rna_TextBox_path(PointerRNA *ptr) +static char *rna_TextBox_path(const PointerRNA *ptr) { - Curve *cu = (Curve *)ptr->owner_id; - TextBox *tb = ptr->data; + const Curve *cu = (Curve *)ptr->owner_id; + const TextBox *tb = ptr->data; int index = (int)(tb - cu->tb); if (index >= 0 && index < cu->totbox) { diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 2b406125409..7cf34db4cf4 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -30,7 +30,7 @@ # include "WM_api.h" # include "WM_types.h" -static Curves *rna_curves(PointerRNA *ptr) +static Curves *rna_curves(const PointerRNA *ptr) { return (Curves *)ptr->owner_id; } @@ -52,13 +52,18 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, NULL); } -static int rna_CurvePoint_index_get(PointerRNA *ptr) +static int rna_CurvePoint_index_get_const(const PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); const float(*co)[3] = ptr->data; return (int)(co - curves->geometry.position); } +static int rna_CurvePoint_index_get(PointerRNA *ptr) +{ + return rna_CurvePoint_index_get_const(ptr); +} + static void rna_CurvePoint_location_get(PointerRNA *ptr, float value[3]) { copy_v3_v3(value, (const float *)ptr->data); @@ -89,20 +94,25 @@ static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value) curves->geometry.radius[co - curves->geometry.position] = value; } -static char *rna_CurvePoint_path(PointerRNA *ptr) +static char *rna_CurvePoint_path(const PointerRNA *ptr) { - return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get(ptr)); + return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get_const(ptr)); } -static int rna_CurveSlice_index_get(PointerRNA *ptr) +static int rna_CurveSlice_index_get_const(const PointerRNA *ptr) { Curves *curves = rna_curves(ptr); return (int)((int *)ptr->data - curves->geometry.curve_offsets); } -static char *rna_CurveSlice_path(PointerRNA *ptr) +static int rna_CurveSlice_index_get(PointerRNA *ptr) +{ + return rna_CurveSlice_index_get_const(ptr); +} + +static char *rna_CurveSlice_path(const PointerRNA *ptr) { - return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get(ptr)); + return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get_const(ptr)); } static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 1e950b883ab..dfb551fcb05 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -3316,6 +3316,33 @@ void RNA_def_property_string_funcs(PropertyRNA *prop, } } +void RNA_def_property_string_search_func(PropertyRNA *prop, + const char *search, + const eStringPropertySearchFlag search_flag) +{ + StructRNA *srna = DefRNA.laststruct; + + if (!DefRNA.preprocess) { + CLOG_ERROR(&LOG, "only during preprocessing."); + return; + } + + switch (prop->type) { + case PROP_STRING: { + StringPropertyRNA *sprop = (StringPropertyRNA *)prop; + sprop->search = (StringPropertySearchFunc)search; + if (search != NULL) { + sprop->search_flag = search_flag | PROP_STRING_SEARCH_SUPPORTED; + } + break; + } + default: + CLOG_ERROR(&LOG, "\"%s.%s\", type is not string.", srna->identifier, prop->identifier); + DefRNA.error = true; + break; + } +} + void RNA_def_property_string_funcs_runtime(PropertyRNA *prop, StringPropertyGetFunc getfunc, StringPropertyLengthFunc lengthfunc, @@ -3343,6 +3370,18 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop, } } +void RNA_def_property_string_search_func_runtime(PropertyRNA *prop, + StringPropertySearchFunc search_fn, + const eStringPropertySearchFlag search_flag) +{ + StringPropertyRNA *sprop = (StringPropertyRNA *)prop; + + sprop->search = search_fn; + if (search_fn != NULL) { + sprop->search_flag = search_flag | PROP_STRING_SEARCH_SUPPORTED; + } +} + void RNA_def_property_pointer_funcs( PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll) { diff --git a/source/blender/makesrna/intern/rna_dynamicpaint.c b/source/blender/makesrna/intern/rna_dynamicpaint.c index ed6d4996c1e..6f9fe3741f7 100644 --- a/source/blender/makesrna/intern/rna_dynamicpaint.c +++ b/source/blender/makesrna/intern/rna_dynamicpaint.c @@ -37,30 +37,30 @@ const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[] = { # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" -static char *rna_DynamicPaintCanvasSettings_path(PointerRNA *ptr) +static char *rna_DynamicPaintCanvasSettings_path(const PointerRNA *ptr) { - DynamicPaintCanvasSettings *settings = (DynamicPaintCanvasSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->pmd; + const DynamicPaintCanvasSettings *settings = (DynamicPaintCanvasSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->pmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].canvas_settings", name_esc); } -static char *rna_DynamicPaintBrushSettings_path(PointerRNA *ptr) +static char *rna_DynamicPaintBrushSettings_path(const PointerRNA *ptr) { - DynamicPaintBrushSettings *settings = (DynamicPaintBrushSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->pmd; + const DynamicPaintBrushSettings *settings = (DynamicPaintBrushSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->pmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].brush_settings", name_esc); } -static char *rna_DynamicPaintSurface_path(PointerRNA *ptr) +static char *rna_DynamicPaintSurface_path(const PointerRNA *ptr) { - DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data; - ModifierData *md = (ModifierData *)surface->canvas->pmd; + const DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data; + const ModifierData *md = (ModifierData *)surface->canvas->pmd; char name_esc[sizeof(md->name) * 2]; char name_esc_surface[sizeof(surface->name) * 2]; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index af14a169d68..e14a291dd01 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -786,11 +786,11 @@ static void rna_FModifier_active_update(Main *bmain, Scene *scene, PointerRNA *p rna_FModifier_update(bmain, scene, ptr); } -static int rna_FModifierGenerator_coefficients_get_length(PointerRNA *ptr, +static int rna_FModifierGenerator_coefficients_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - FModifier *fcm = (FModifier *)ptr->data; - FMod_Generator *gen = fcm->data; + const FModifier *fcm = (FModifier *)ptr->data; + const FMod_Generator *gen = fcm->data; if (gen) { length[0] = gen->arraysize; diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index e0ec146a248..3b22ae9d40f 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -857,30 +857,30 @@ static void rna_Fluid_domaintype_set(struct PointerRNA *ptr, int value) BKE_fluid_fields_sanitize(settings); } -static char *rna_FluidDomainSettings_path(PointerRNA *ptr) +static char *rna_FluidDomainSettings_path(const PointerRNA *ptr) { - FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->fmd; + const FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->fmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].domain_settings", name_esc); } -static char *rna_FluidFlowSettings_path(PointerRNA *ptr) +static char *rna_FluidFlowSettings_path(const PointerRNA *ptr) { - FluidFlowSettings *settings = (FluidFlowSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->fmd; + const FluidFlowSettings *settings = (FluidFlowSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->fmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].flow_settings", name_esc); } -static char *rna_FluidEffectorSettings_path(PointerRNA *ptr) +static char *rna_FluidEffectorSettings_path(const PointerRNA *ptr) { - FluidEffectorSettings *settings = (FluidEffectorSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->fmd; + const FluidEffectorSettings *settings = (FluidEffectorSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->fmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); @@ -893,9 +893,10 @@ static char *rna_FluidEffectorSettings_path(PointerRNA *ptr) # ifdef WITH_FLUID -static int rna_FluidModifier_grid_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_FluidModifier_grid_get_length(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; + const FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; float *density = NULL; int size = 0; @@ -918,7 +919,7 @@ static int rna_FluidModifier_grid_get_length(PointerRNA *ptr, int length[RNA_MAX return length[0]; } -static int rna_FluidModifier_color_grid_get_length(PointerRNA *ptr, +static int rna_FluidModifier_color_grid_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { rna_FluidModifier_grid_get_length(ptr, length); @@ -927,10 +928,10 @@ static int rna_FluidModifier_color_grid_get_length(PointerRNA *ptr, return length[0]; } -static int rna_FluidModifier_velocity_grid_get_length(PointerRNA *ptr, +static int rna_FluidModifier_velocity_grid_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; + const FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; float *vx = NULL; float *vy = NULL; float *vz = NULL; @@ -948,10 +949,10 @@ static int rna_FluidModifier_velocity_grid_get_length(PointerRNA *ptr, return length[0]; } -static int rna_FluidModifier_heat_grid_get_length(PointerRNA *ptr, +static int rna_FluidModifier_heat_grid_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; + const FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; float *heat = NULL; int size = 0; diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 9a9b6d582e5..6854ce37c94 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -307,7 +307,7 @@ bool rna_GPencil_datablocks_obdata_poll(PointerRNA *UNUSED(ptr), const PointerRN return (gpd->flag & GP_DATA_ANNOTATIONS) == 0; } -static char *rna_GPencilLayer_path(PointerRNA *ptr) +static char *rna_GPencilLayer_path(const PointerRNA *ptr) { bGPDlayer *gpl = (bGPDlayer *)ptr->data; char name_esc[sizeof(gpl->info) * 2]; @@ -407,7 +407,7 @@ static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value) } } -static char *rna_GPencilLayerMask_path(PointerRNA *ptr) +static char *rna_GPencilLayerMask_path(const PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->owner_id; bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); @@ -1122,7 +1122,7 @@ static void rna_GPencil_clear(bGPdata *gpd) WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } -static char *rna_GreasePencilGrid_path(PointerRNA *UNUSED(ptr)) +static char *rna_GreasePencilGrid_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("grid"); } diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 778f5647417..8bbc33d2381 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -195,6 +195,7 @@ static const EnumPropertyItem rna_enum_time_mode_items[] = { {GP_TIME_MODE_NORMAL, "NORMAL", 0, "Regular", "Apply offset in usual animation direction"}, {GP_TIME_MODE_REVERSE, "REVERSE", 0, "Reverse", "Apply offset in reverse animation direction"}, {GP_TIME_MODE_FIX, "FIX", 0, "Fixed Frame", "Keep frame and do not change with time"}, + {GP_TIME_MODE_PINGPONG, "PINGPONG", 0, "Ping Pong", "Loop back and forth"}, {0, NULL, 0, NULL, NULL}, }; @@ -340,9 +341,9 @@ static void rna_GpencilModifier_name_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(NULL, "grease_pencil_modifiers", oldname, gmd->name); } -static char *rna_GpencilModifier_path(PointerRNA *ptr) +static char *rna_GpencilModifier_path(const PointerRNA *ptr) { - GpencilModifierData *gmd = ptr->data; + const GpencilModifierData *gmd = ptr->data; char name_esc[sizeof(gmd->name) * 2]; BLI_str_escape(name_esc, gmd->name, sizeof(name_esc)); @@ -751,11 +752,11 @@ static void rna_GpencilDash_segments_begin(CollectionPropertyIterator *iter, Poi iter, dmd->segments, sizeof(DashGpencilModifierSegment), dmd->segments_len, false, NULL); } -static char *rna_DashGpencilModifierSegment_path(PointerRNA *ptr) +static char *rna_DashGpencilModifierSegment_path(const PointerRNA *ptr) { - DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; + const DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; - DashGpencilModifierData *dmd = (DashGpencilModifierData *)ds->dmd; + const DashGpencilModifierData *dmd = (DashGpencilModifierData *)ds->dmd; BLI_assert(dmd != NULL); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 81708ac8e65..269ebe1581f 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -175,7 +175,7 @@ static void rna_ImageUser_relations_update(Main *bmain, Scene *scene, PointerRNA DEG_relations_tag_update(bmain); } -static char *rna_ImageUser_path(PointerRNA *ptr) +static char *rna_ImageUser_path(const PointerRNA *ptr) { if (ptr->owner_id) { /* ImageUser *iuser = ptr->data; */ @@ -397,7 +397,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } @@ -446,7 +446,7 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr) return duration; } -static int rna_Image_pixels_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_Image_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { Image *ima = (Image *)ptr->owner_id; ImBuf *ibuf; diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index b385f576585..bac8f214441 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -156,6 +156,7 @@ static void rna_Image_update(Image *image, ReportList *reports) } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + BKE_image_partial_update_mark_full_update(image); BKE_image_release_ibuf(image, ibuf, NULL); } @@ -197,7 +198,7 @@ static int rna_Image_gl_touch( BKE_image_tag_time(image); - if (image->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL] == NULL) { + if (image->gputexture[TEXTARGET_2D][0] == NULL) { error = rna_Image_gl_load(image, reports, frame, layer_index, pass_index); } diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index bf4eec433c4..058c63f640a 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -373,7 +373,7 @@ void rna_ViewLayer_active_lightgroup_index_set(PointerRNA *ptr, int value); * `rna_path_buffer_size` should be at least `sizeof(ViewLayer.name) * 3`. * \return actual length of the generated RNA path. */ -size_t rna_ViewLayer_path_buffer_get(struct ViewLayer *view_layer, +size_t rna_ViewLayer_path_buffer_get(const struct ViewLayer *view_layer, char *r_rna_path, const size_t rna_path_buffer_size); @@ -402,8 +402,8 @@ bool rna_GPencil_datablocks_annotations_poll(struct PointerRNA *ptr, const struct PointerRNA value); bool rna_GPencil_datablocks_obdata_poll(struct PointerRNA *ptr, const struct PointerRNA value); -char *rna_TextureSlot_path(struct PointerRNA *ptr); -char *rna_Node_ImageUser_path(struct PointerRNA *ptr); +char *rna_TextureSlot_path(const struct PointerRNA *ptr); +char *rna_Node_ImageUser_path(const struct PointerRNA *ptr); /* Set U.is_dirty and redraw. */ diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 4db438f04b4..4f88959b5ba 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -50,9 +50,10 @@ typedef int (*EditableFunc)(struct PointerRNA *ptr, const char **r_info); typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index); typedef struct IDProperty **(*IDPropertiesFunc)(struct PointerRNA *ptr); typedef struct StructRNA *(*StructRefineFunc)(struct PointerRNA *ptr); -typedef char *(*StructPathFunc)(struct PointerRNA *ptr); +typedef char *(*StructPathFunc)(const struct PointerRNA *ptr); -typedef int (*PropArrayLengthGetFunc)(struct PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]); +typedef int (*PropArrayLengthGetFunc)(const struct PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]); typedef bool (*PropBooleanGetFunc)(struct PointerRNA *ptr); typedef void (*PropBooleanSetFunc)(struct PointerRNA *ptr, bool value); typedef void (*PropBooleanArrayGetFunc)(struct PointerRNA *ptr, bool *values); @@ -439,6 +440,15 @@ typedef struct StringPropertyRNA { PropStringLengthFuncEx length_ex; PropStringSetFuncEx set_ex; + /** + * Optional callback to list candidates for a string. + * This is only for use as suggestions in UI, other values may be assigned. + * + * \note The callback type is public, hence the difference in naming convention. + */ + StringPropertySearchFunc search; + eStringPropertySearchFlag search_flag; + int maxlength; /* includes string terminator! */ const char *defaultvalue; diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 50b25157989..2f6fb30dc49 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -159,7 +159,7 @@ static void rna_ShapeKey_slider_max_set(PointerRNA *ptr, float value) * such case looks rather unlikely - and not worth adding some kind of caching in key-blocks. */ -static Mesh *rna_KeyBlock_normals_get_mesh(PointerRNA *ptr, ID *id) +static Mesh *rna_KeyBlock_normals_get_mesh(const PointerRNA *ptr, ID *id) { Key *key = rna_ShapeKey_find_key((id == NULL && ptr != NULL) ? ptr->owner_id : id); id = key ? key->from : NULL; @@ -182,9 +182,10 @@ static Mesh *rna_KeyBlock_normals_get_mesh(PointerRNA *ptr, ID *id) return NULL; } -static int rna_KeyBlock_normals_vert_len(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_KeyBlock_normals_vert_len(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); + const Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); length[0] = me ? me->totvert : 0; length[1] = 3; @@ -211,9 +212,10 @@ static void rna_KeyBlock_normals_vert_calc(ID *id, BKE_keyblock_mesh_calc_normals(data, me, (float(*)[3])(*normals), NULL, NULL); } -static int rna_KeyBlock_normals_poly_len(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_KeyBlock_normals_poly_len(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); + const Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); length[0] = me ? me->totpoly : 0; length[1] = 3; @@ -240,9 +242,10 @@ static void rna_KeyBlock_normals_poly_calc(ID *id, BKE_keyblock_mesh_calc_normals(data, me, NULL, (float(*)[3])(*normals), NULL); } -static int rna_KeyBlock_normals_loop_len(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_KeyBlock_normals_loop_len(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); + const Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); length[0] = me ? me->totloop : 0; length[1] = 3; @@ -656,10 +659,10 @@ int rna_ShapeKey_data_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) return false; } -static char *rna_ShapeKey_path(PointerRNA *ptr) +static char *rna_ShapeKey_path(const PointerRNA *ptr) { - KeyBlock *kb = (KeyBlock *)ptr->data; - ID *id = ptr->owner_id; + const KeyBlock *kb = (KeyBlock *)ptr->data; + const ID *id = ptr->owner_id; char name_esc[sizeof(kb->name) * 2]; BLI_str_escape(name_esc, kb->name, sizeof(name_esc)); @@ -750,7 +753,7 @@ static int rna_ShapeKeyPoint_get_index(Key *key, KeyBlock *kb, float *point) return (int)(pt - start) / key->elemsize; } -static char *rna_ShapeKeyPoint_path(PointerRNA *ptr) +static char *rna_ShapeKeyPoint_path(const PointerRNA *ptr) { ID *id = ptr->owner_id; Key *key = rna_ShapeKey_find_key(ptr->owner_id); diff --git a/source/blender/makesrna/intern/rna_lattice.c b/source/blender/makesrna/intern/rna_lattice.c index 8fb36c035b7..f94804cb609 100644 --- a/source/blender/makesrna/intern/rna_lattice.c +++ b/source/blender/makesrna/intern/rna_lattice.c @@ -209,11 +209,11 @@ static void rna_Lattice_vg_name_set(PointerRNA *ptr, const char *value) } /* annoying, but is a consequence of RNA structures... */ -static char *rna_LatticePoint_path(PointerRNA *ptr) +static char *rna_LatticePoint_path(const PointerRNA *ptr) { - Lattice *lt = (Lattice *)ptr->owner_id; - void *point = ptr->data; - BPoint *points = NULL; + const Lattice *lt = (Lattice *)ptr->owner_id; + const void *point = ptr->data; + const BPoint *points = NULL; if (lt->editlatt && lt->editlatt->latt->def) { points = lt->editlatt->latt->def; diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index 42414a9931b..8c9c66bffcf 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -98,7 +98,7 @@ static void rna_LayerObjects_active_object_set(PointerRNA *ptr, } } -size_t rna_ViewLayer_path_buffer_get(ViewLayer *view_layer, +size_t rna_ViewLayer_path_buffer_get(const ViewLayer *view_layer, char *r_rna_path, const size_t rna_path_buffer_size) { @@ -108,9 +108,9 @@ size_t rna_ViewLayer_path_buffer_get(ViewLayer *view_layer, return BLI_snprintf_rlen(r_rna_path, rna_path_buffer_size, "view_layers[\"%s\"]", name_esc); } -static char *rna_ViewLayer_path(PointerRNA *ptr) +static char *rna_ViewLayer_path(const PointerRNA *ptr) { - ViewLayer *view_layer = (ViewLayer *)ptr->data; + const ViewLayer *view_layer = (ViewLayer *)ptr->data; char rna_path[sizeof(view_layer->name) * 3]; rna_ViewLayer_path_buffer_get(view_layer, rna_path, sizeof(rna_path)); diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index 2a8a3a76c6d..c18ee461fae 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -239,33 +239,33 @@ static StructRNA *rna_LineStyle_geometry_modifier_refine(struct PointerRNA *ptr) } } -static char *rna_LineStyle_color_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_color_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("color_modifiers[\"%s\"]", name_esc); } -static char *rna_LineStyle_alpha_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_alpha_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("alpha_modifiers[\"%s\"]", name_esc); } -static char *rna_LineStyle_thickness_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_thickness_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("thickness_modifiers[\"%s\"]", name_esc); } -static char *rna_LineStyle_geometry_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_geometry_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("geometry_modifiers[\"%s\"]", name_esc); diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index 2247a16a7a0..3dac148f7d4 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -165,9 +165,9 @@ static void rna_Mask_layer_active_index_range( *softmax = *max; } -static char *rna_MaskLayer_path(PointerRNA *ptr) +static char *rna_MaskLayer_path(const PointerRNA *ptr) { - MaskLayer *masklay = (MaskLayer *)ptr->data; + const MaskLayer *masklay = (MaskLayer *)ptr->data; char name_esc[sizeof(masklay->name) * 2]; BLI_str_escape(name_esc, masklay->name, sizeof(name_esc)); return BLI_sprintfN("layers[\"%s\"]", name_esc); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 15e7e12bbf8..1de144d81ef 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -362,7 +362,7 @@ static void rna_gpcolordata_uv_update(Main *bmain, Scene *scene, PointerRNA *ptr rna_MaterialGpencil_update(bmain, scene, ptr); } -static char *rna_GpencilColorData_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpencilColorData_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("grease_pencil"); } diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 2ba3cf1096c..da4be3cd0da 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -71,7 +71,7 @@ static const EnumPropertyItem rna_enum_mesh_remesh_mode_items[] = { /** \name Generic Helpers * \{ */ -static Mesh *rna_mesh(PointerRNA *ptr) +static Mesh *rna_mesh(const PointerRNA *ptr) { Mesh *me = (Mesh *)ptr->owner_id; return me; @@ -102,7 +102,7 @@ static CustomData *rna_mesh_fdata_helper(Mesh *me) return (me->edit_mesh) ? NULL : &me->fdata; } -static CustomData *rna_mesh_vdata(PointerRNA *ptr) +static CustomData *rna_mesh_vdata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_vdata_helper(me); @@ -114,13 +114,13 @@ static CustomData *rna_mesh_edata(PointerRNA *ptr) return rna_mesh_edata_helper(me); } # endif -static CustomData *rna_mesh_pdata(PointerRNA *ptr) +static CustomData *rna_mesh_pdata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_pdata_helper(me); } -static CustomData *rna_mesh_ldata(PointerRNA *ptr) +static CustomData *rna_mesh_ldata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_ldata_helper(me); @@ -689,9 +689,9 @@ DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(uv_layer, ldata, CD_MLOOPUV, rende /* MeshUVLoopLayer */ -static char *rna_MeshUVLoopLayer_path(PointerRNA *ptr) +static char *rna_MeshUVLoopLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("uv_layers[\"%s\"]", name_esc); @@ -932,16 +932,16 @@ static int rna_Mesh_polygon_string_layers_length(PointerRNA *ptr) /* Skin vertices */ DEFINE_CUSTOMDATA_LAYER_COLLECTION(skin_vertice, vdata, CD_MVERT_SKIN) -static char *rna_MeshSkinVertexLayer_path(PointerRNA *ptr) +static char *rna_MeshSkinVertexLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("skin_vertices[\"%s\"]", name_esc); } -static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collection, int type); -static char *rna_MeshSkinVertex_path(PointerRNA *ptr) +static char *rna_VertCustomData_data_path(const PointerRNA *ptr, const char *collection, int type); +static char *rna_MeshSkinVertex_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "skin_vertices", CD_MVERT_SKIN); } @@ -964,8 +964,7 @@ static int rna_MeshSkinVertexLayer_data_length(PointerRNA *ptr) /* Vertex creases */ DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_crease, vdata, CD_CREASE) -static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collection, int type); -static char *rna_MeshVertexCreaseLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexCreaseLayer_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_creases", CD_CREASE); } @@ -988,15 +987,15 @@ static int rna_MeshVertexCreaseLayer_data_length(PointerRNA *ptr) /* Paint mask */ DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_paint_mask, vdata, CD_PAINT_MASK) -static char *rna_MeshPaintMaskLayer_path(PointerRNA *ptr) +static char *rna_MeshPaintMaskLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_paint_masks[\"%s\"]", name_esc); } -static char *rna_MeshPaintMask_path(PointerRNA *ptr) +static char *rna_MeshPaintMask_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_paint_masks", CD_PAINT_MASK); } @@ -1023,9 +1022,9 @@ DEFINE_CUSTOMDATA_LAYER_COLLECTION(face_map, pdata, CD_FACEMAP) DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM( face_map, pdata, CD_FACEMAP, active, MeshFaceMapLayer) -static char *rna_MeshFaceMapLayer_path(PointerRNA *ptr) +static char *rna_MeshFaceMapLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("face_maps[\"%s\"]", name_esc); @@ -1090,9 +1089,10 @@ static void rna_Mesh_face_map_remove(struct Mesh *me, /* End face maps */ /* poly.vertices - this is faked loop access for convenience */ -static int rna_MeshPoly_vertices_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_MeshPoly_vertices_get_length(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - MPoly *mp = (MPoly *)ptr->data; + const MPoly *mp = (MPoly *)ptr->data; /* NOTE: raw access uses dummy item, this _could_ crash, * watch out for this, mface uses it but it can't work here. */ return (length[0] = mp->totloop); @@ -1182,11 +1182,11 @@ static int rna_MeshLoop_index_get(PointerRNA *ptr) /* path construction */ -static char *rna_VertexGroupElement_path(PointerRNA *ptr) +static char *rna_VertexGroupElement_path(const PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); /* XXX not always! */ - MDeformWeight *dw = (MDeformWeight *)ptr->data; - MDeformVert *dvert; + const Mesh *me = rna_mesh(ptr); /* XXX not always! */ + const MDeformWeight *dw = (MDeformWeight *)ptr->data; + const MDeformVert *dvert; int a, b; for (a = 0, dvert = me->dvert; a < me->totvert; a++, dvert++) { @@ -1200,37 +1200,37 @@ static char *rna_VertexGroupElement_path(PointerRNA *ptr) return NULL; } -static char *rna_MeshPolygon_path(PointerRNA *ptr) +static char *rna_MeshPolygon_path(const PointerRNA *ptr) { return BLI_sprintfN("polygons[%d]", (int)((MPoly *)ptr->data - rna_mesh(ptr)->mpoly)); } -static char *rna_MeshLoopTriangle_path(PointerRNA *ptr) +static char *rna_MeshLoopTriangle_path(const PointerRNA *ptr) { return BLI_sprintfN("loop_triangles[%d]", (int)((MLoopTri *)ptr->data - rna_mesh(ptr)->runtime.looptris.array)); } -static char *rna_MeshEdge_path(PointerRNA *ptr) +static char *rna_MeshEdge_path(const PointerRNA *ptr) { return BLI_sprintfN("edges[%d]", (int)((MEdge *)ptr->data - rna_mesh(ptr)->medge)); } -static char *rna_MeshLoop_path(PointerRNA *ptr) +static char *rna_MeshLoop_path(const PointerRNA *ptr) { return BLI_sprintfN("loops[%d]", (int)((MLoop *)ptr->data - rna_mesh(ptr)->mloop)); } -static char *rna_MeshVertex_path(PointerRNA *ptr) +static char *rna_MeshVertex_path(const PointerRNA *ptr) { return BLI_sprintfN("vertices[%d]", (int)((MVert *)ptr->data - rna_mesh(ptr)->mvert)); } -static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collection, int type) +static char *rna_VertCustomData_data_path(const PointerRNA *ptr, const char *collection, int type) { - CustomDataLayer *cdl; - Mesh *me = rna_mesh(ptr); - CustomData *vdata = rna_mesh_vdata(ptr); + const CustomDataLayer *cdl; + const Mesh *me = rna_mesh(ptr); + const CustomData *vdata = rna_mesh_vdata(ptr); int a, b, totvert = (me->edit_mesh) ? 0 : me->totvert; for (cdl = vdata->layers, a = 0; a < vdata->totlayer; cdl++, a++) { @@ -1247,11 +1247,11 @@ static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collectio return NULL; } -static char *rna_PolyCustomData_data_path(PointerRNA *ptr, const char *collection, int type) +static char *rna_PolyCustomData_data_path(const PointerRNA *ptr, const char *collection, int type) { - CustomDataLayer *cdl; - Mesh *me = rna_mesh(ptr); - CustomData *pdata = rna_mesh_pdata(ptr); + const CustomDataLayer *cdl; + const Mesh *me = rna_mesh(ptr); + const CustomData *pdata = rna_mesh_pdata(ptr); int a, b, totpoly = (me->edit_mesh) ? 0 : me->totpoly; for (cdl = pdata->layers, a = 0; a < pdata->totlayer; cdl++, a++) { @@ -1268,11 +1268,11 @@ static char *rna_PolyCustomData_data_path(PointerRNA *ptr, const char *collectio return NULL; } -static char *rna_LoopCustomData_data_path(PointerRNA *ptr, const char *collection, int type) +static char *rna_LoopCustomData_data_path(const PointerRNA *ptr, const char *collection, int type) { - CustomDataLayer *cdl; - Mesh *me = rna_mesh(ptr); - CustomData *ldata = rna_mesh_ldata(ptr); + const CustomDataLayer *cdl; + const Mesh *me = rna_mesh(ptr); + const CustomData *ldata = rna_mesh_ldata(ptr); int a, b, totloop = (me->edit_mesh) ? 0 : me->totloop; for (cdl = ldata->layers, a = 0; a < ldata->totlayer; cdl++, a++) { @@ -1315,58 +1315,58 @@ static int rna_Mesh_poly_normals_length(PointerRNA *ptr) return mesh->totpoly; } -static char *rna_MeshUVLoop_path(PointerRNA *ptr) +static char *rna_MeshUVLoop_path(const PointerRNA *ptr) { return rna_LoopCustomData_data_path(ptr, "uv_layers", CD_MLOOPUV); } -static char *rna_MeshLoopColorLayer_path(PointerRNA *ptr) +static char *rna_MeshLoopColorLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_colors[\"%s\"]", name_esc); } -static char *rna_MeshColor_path(PointerRNA *ptr) +static char *rna_MeshColor_path(const PointerRNA *ptr) { return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_PROP_BYTE_COLOR); } -static char *rna_MeshVertColorLayer_path(PointerRNA *ptr) +static char *rna_MeshVertColorLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("sculpt_vertex_colors[\"%s\"]", name_esc); } -static char *rna_MeshVertColor_path(PointerRNA *ptr) +static char *rna_MeshVertColor_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "sculpt_vertex_colors", CD_PROP_COLOR); } /**** Float Property Layer API ****/ -static char *rna_MeshVertexFloatPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexFloatPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_float_layers[\"%s\"]", name_esc); } -static char *rna_MeshPolygonFloatPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshPolygonFloatPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("polygon_float_layers[\"%s\"]", name_esc); } -static char *rna_MeshVertexFloatProperty_path(PointerRNA *ptr) +static char *rna_MeshVertexFloatProperty_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_layers_float", CD_PROP_FLOAT); } -static char *rna_MeshPolygonFloatProperty_path(PointerRNA *ptr) +static char *rna_MeshPolygonFloatProperty_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "polygon_layers_float", CD_PROP_FLOAT); } @@ -1398,26 +1398,26 @@ static int rna_MeshPolygonFloatPropertyLayer_data_length(PointerRNA *ptr) } /**** Int Property Layer API ****/ -static char *rna_MeshVertexIntPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexIntPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_int_layers[\"%s\"]", name_esc); } -static char *rna_MeshPolygonIntPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshPolygonIntPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("polygon_int_layers[\"%s\"]", name_esc); } -static char *rna_MeshVertexIntProperty_path(PointerRNA *ptr) +static char *rna_MeshVertexIntProperty_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_layers_int", CD_PROP_INT32); } -static char *rna_MeshPolygonIntProperty_path(PointerRNA *ptr) +static char *rna_MeshPolygonIntProperty_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "polygon_layers_int", CD_PROP_INT32); } @@ -1449,26 +1449,26 @@ static int rna_MeshPolygonIntPropertyLayer_data_length(PointerRNA *ptr) } /**** String Property Layer API ****/ -static char *rna_MeshVertexStringPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexStringPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_string_layers[\"%s\"]", name_esc); } -static char *rna_MeshPolygonStringPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshPolygonStringPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("polygon_string_layers[\"%s\"]", name_esc); } -static char *rna_MeshVertexStringProperty_path(PointerRNA *ptr) +static char *rna_MeshVertexStringProperty_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_layers_string", CD_PROP_STRING); } -static char *rna_MeshPolygonStringProperty_path(PointerRNA *ptr) +static char *rna_MeshPolygonStringProperty_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "polygon_layers_string", CD_PROP_STRING); } @@ -1519,7 +1519,7 @@ void rna_MeshStringProperty_s_set(PointerRNA *ptr, const char *value) BLI_strncpy(ms->s, value, sizeof(ms->s)); } -static char *rna_MeshFaceMap_path(PointerRNA *ptr) +static char *rna_MeshFaceMap_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "face_maps", CD_FACEMAP); } @@ -1638,7 +1638,7 @@ static PointerRNA rna_Mesh_uv_layers_new(struct Mesh *me, PointerRNA ptr; CustomData *ldata; CustomDataLayer *cdl = NULL; - int index = ED_mesh_uv_texture_add(me, name, false, do_init, reports); + int index = ED_mesh_uv_add(me, name, false, do_init, reports); if (index != -1) { ldata = rna_mesh_ldata_helper(me); @@ -1651,7 +1651,7 @@ static PointerRNA rna_Mesh_uv_layers_new(struct Mesh *me, static void rna_Mesh_uv_layers_remove(struct Mesh *me, ReportList *reports, CustomDataLayer *layer) { - if (ED_mesh_uv_texture_remove_named(me, layer->name) == false) { + if (ED_mesh_uv_remove_named(me, layer->name) == false) { BKE_reportf(reports, RPT_ERROR, "Texture layer '%s' not found", layer->name); } } @@ -2197,7 +2197,7 @@ static void rna_def_mloopcol(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_float_funcs( prop, "rna_MeshLoopColor_color_get", "rna_MeshLoopColor_color_set", NULL); - RNA_def_property_ui_text(prop, "Color", ""); + RNA_def_property_ui_text(prop, "Color", "Color in sRGB color space"); RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); } @@ -3212,7 +3212,9 @@ static void rna_def_mesh(BlenderRNA *brna) NULL); RNA_def_property_struct_type(prop, "MeshLoopColorLayer"); RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); - RNA_def_property_ui_text(prop, "Vertex Colors", "All vertex colors"); + RNA_def_property_ui_text(prop, + "Vertex Colors", + "Legacy vertex color layers. Deprecated, use color attributes instead"); rna_def_loop_colors(brna, prop); /* Sculpt Vertex colors */ @@ -3230,7 +3232,9 @@ static void rna_def_mesh(BlenderRNA *brna) NULL); RNA_def_property_struct_type(prop, "MeshVertColorLayer"); RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); - RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors"); + RNA_def_property_ui_text(prop, + "Sculpt Vertex Colors", + "Sculpt vertex color layers. Deprecated, use color attributes instead"); rna_def_vert_colors(brna, prop); /* TODO: edge customdata layers (bmesh py api can access already). */ diff --git a/source/blender/makesrna/intern/rna_meta.c b/source/blender/makesrna/intern/rna_meta.c index f160388c5d2..e6cf743e167 100644 --- a/source/blender/makesrna/intern/rna_meta.c +++ b/source/blender/makesrna/intern/rna_meta.c @@ -156,10 +156,10 @@ static bool rna_Meta_is_editmode_get(PointerRNA *ptr) return (mb->editelems != NULL); } -static char *rna_MetaElement_path(PointerRNA *ptr) +static char *rna_MetaElement_path(const PointerRNA *ptr) { - MetaBall *mb = (MetaBall *)ptr->owner_id; - MetaElem *ml = ptr->data; + const MetaBall *mb = (MetaBall *)ptr->owner_id; + const MetaElem *ml = ptr->data; int index = -1; if (mb->editelems) { diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 6dd7fe53774..992e306b2bb 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -722,9 +722,9 @@ static void rna_Modifier_name_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name); } -static char *rna_Modifier_path(PointerRNA *ptr) +static char *rna_Modifier_path(const PointerRNA *ptr) { - ModifierData *md = ptr->data; + const ModifierData *md = ptr->data; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); @@ -947,10 +947,10 @@ static void rna_HookModifier_subtarget_set(PointerRNA *ptr, const char *value) BKE_object_modifier_hook_reset(owner, hmd); } -static int rna_HookModifier_vertex_indices_get_length(PointerRNA *ptr, +static int rna_HookModifier_vertex_indices_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - HookModifierData *hmd = ptr->data; + const HookModifierData *hmd = ptr->data; int indexar_num = hmd->indexar ? hmd->indexar_num : 0; return (length[0] = indexar_num); } diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index f8a35246581..8aa87c1bcaa 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -57,7 +57,7 @@ static void rna_NlaStrip_name_set(PointerRNA *ptr, const char *value) } } -static char *rna_NlaStrip_path(PointerRNA *ptr) +static char *rna_NlaStrip_path(const PointerRNA *ptr) { NlaStrip *strip = (NlaStrip *)ptr->data; AnimData *adt = BKE_animdata_from_id(ptr->owner_id); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 9b9afadbd05..b9da7a2435e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1598,16 +1598,16 @@ static StructRNA *rna_Node_refine(struct PointerRNA *ptr) } } -static char *rna_Node_path(PointerRNA *ptr) +static char *rna_Node_path(const PointerRNA *ptr) { - bNode *node = (bNode *)ptr->data; + const bNode *node = (bNode *)ptr->data; char name_esc[sizeof(node->name) * 2]; BLI_str_escape(name_esc, node->name, sizeof(name_esc)); return BLI_sprintfN("nodes[\"%s\"]", name_esc); } -char *rna_Node_ImageUser_path(PointerRNA *ptr) +char *rna_Node_ImageUser_path(const PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node; @@ -2756,7 +2756,7 @@ static StructRNA *rna_NodeSocket_refine(PointerRNA *ptr) } } -static char *rna_NodeSocket_path(PointerRNA *ptr) +static char *rna_NodeSocket_path(const PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNodeSocket *sock = (bNodeSocket *)ptr->data; @@ -3070,10 +3070,10 @@ static StructRNA *rna_NodeSocketInterface_refine(PointerRNA *ptr) } } -static char *rna_NodeSocketInterface_path(PointerRNA *ptr) +static char *rna_NodeSocketInterface_path(const PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = (bNodeSocket *)ptr->data; + const bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + const bNodeSocket *sock = (bNodeSocket *)ptr->data; int socketindex; socketindex = BLI_findindex(&ntree->inputs, sock); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 1fb41bf792f..b192a385546 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1320,12 +1320,17 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index) return PROP_EDITABLE; } -static int rna_MaterialSlot_index(PointerRNA *ptr) +static int rna_MaterialSlot_index(const PointerRNA *ptr) { /* There is an offset, so that `ptr->data` is not null and unique across IDs. */ return (uintptr_t)ptr->data - (uintptr_t)ptr->owner_id; } +static int rna_MaterialSlot_index_get(PointerRNA *ptr) +{ + return rna_MaterialSlot_index(ptr); +} + static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info)) { Object *ob = (Object *)ptr->owner_id; @@ -1451,7 +1456,7 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr) DEG_relations_tag_update(bmain); } -static char *rna_MaterialSlot_path(PointerRNA *ptr) +static char *rna_MaterialSlot_path(const PointerRNA *ptr) { int index = rna_MaterialSlot_index(ptr); return BLI_sprintfN("material_slots[%d]", index); @@ -1504,7 +1509,7 @@ static PointerRNA rna_Object_display_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_ObjectDisplay, ptr->data); } -static char *rna_ObjectDisplay_path(PointerRNA *UNUSED(ptr)) +static char *rna_ObjectDisplay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("display"); } @@ -1829,6 +1834,24 @@ bool rna_Object_modifiers_override_apply(Main *bmain, ModifierData *mod_dst = ED_object_modifier_add( NULL, bmain, NULL, ob_dst, mod_src->name, mod_src->type); + if (mod_dst == NULL) { + /* This can happen e.g. when a modifier type is tagged as `eModifierTypeFlag_Single`, and that + * modifier has somehow been added already by another code path (e.g. + * `rna_CollisionSettings_dependency_update` does add the `eModifierType_Collision` singleton + * modifier). + * + * Try to handle this by finding already existing one here. */ + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)mod_src->type); + if (mti->flags & eModifierTypeFlag_Single) { + mod_dst = BKE_modifiers_findby_type(ob_dst, (ModifierType)mod_src->type); + } + + if (mod_dst == NULL) { + BLI_assert(mod_src != NULL); + return false; + } + } + /* XXX Current handling of 'copy' from particle-system modifier is *very* bad (it keeps same psys * pointer as source, then calling code copies psys of object separately and do some magic * remapping of pointers...), unfortunately several pieces of code in Object editing area rely on @@ -2466,7 +2489,7 @@ static void rna_def_material_slot(BlenderRNA *brna) prop = RNA_def_property(srna, "slot_index", PROP_INT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_int_funcs(prop, "rna_MaterialSlot_index", NULL, NULL); + RNA_def_property_int_funcs(prop, "rna_MaterialSlot_index_get", NULL, NULL); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs( diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index addc8ac0c6c..2ed539aa511 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -130,7 +130,7 @@ static bool rna_Cache_get_valid_owner_ID(PointerRNA *ptr, Object **ob, Scene **s return (*ob != NULL || *scene != NULL); } -static char *rna_PointCache_path(PointerRNA *ptr) +static char *rna_PointCache_path(const PointerRNA *ptr) { ModifierData *md; Object *ob = (Object *)ptr->owner_id; @@ -443,7 +443,7 @@ int rna_Cache_info_length(PointerRNA *ptr) return (int)strlen(cache->info); } -static char *rna_CollisionSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_CollisionSettings_path(const PointerRNA *UNUSED(ptr)) { /* both methods work ok, but return the shorter path */ # if 0 @@ -619,17 +619,17 @@ static void rna_SoftBodySettings_spring_vgroup_set(PointerRNA *ptr, const char * rna_object_vgroup_name_set(ptr, value, sb->namedVG_Spring_K, sizeof(sb->namedVG_Spring_K)); } -static char *rna_SoftBodySettings_path(PointerRNA *ptr) +static char *rna_SoftBodySettings_path(const PointerRNA *ptr) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = (ModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Softbody); + const Object *ob = (Object *)ptr->owner_id; + const ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Softbody); char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].settings", name_esc); } -static int particle_id_check(PointerRNA *ptr) +static int particle_id_check(const PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -731,7 +731,7 @@ static void rna_FieldSettings_dependency_update(Main *bmain, Scene *scene, Point } } -static char *rna_FieldSettings_path(PointerRNA *ptr) +static char *rna_FieldSettings_path(const PointerRNA *ptr) { PartDeflect *pd = (PartDeflect *)ptr->data; @@ -787,7 +787,7 @@ static void rna_EffectorWeight_dependency_update(Main *bmain, WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); } -static char *rna_EffectorWeight_path(PointerRNA *ptr) +static char *rna_EffectorWeight_path(const PointerRNA *ptr) { EffectorWeights *ew = (EffectorWeights *)ptr->data; /* Check through all possible places the settings can be to find the right one */ diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 8a024fdcfc2..a67b0f7c8e6 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -1208,19 +1208,19 @@ static int rna_ParticleTarget_name_length(PointerRNA *ptr) return strlen(tstr); } -static int particle_id_check(PointerRNA *ptr) +static int particle_id_check(const PointerRNA *ptr) { - ID *id = ptr->owner_id; + const ID *id = ptr->owner_id; return (GS(id->name) == ID_PA); } -static char *rna_SPHFluidSettings_path(PointerRNA *ptr) +static char *rna_SPHFluidSettings_path(const PointerRNA *ptr) { - SPHFluidSettings *fluid = (SPHFluidSettings *)ptr->data; + const SPHFluidSettings *fluid = (SPHFluidSettings *)ptr->data; if (particle_id_check(ptr)) { - ParticleSettings *part = (ParticleSettings *)ptr->owner_id; + const ParticleSettings *part = (ParticleSettings *)ptr->owner_id; if (part->fluid == fluid) { return BLI_strdup("fluid"); @@ -1463,9 +1463,9 @@ static void psys_vg_name_set__internal(PointerRNA *ptr, const char *value, int i } } -static char *rna_ParticleSystem_path(PointerRNA *ptr) +static char *rna_ParticleSystem_path(const PointerRNA *ptr) { - ParticleSystem *psys = (ParticleSystem *)ptr->data; + const ParticleSystem *psys = (ParticleSystem *)ptr->data; char name_esc[sizeof(psys->name) * 2]; BLI_str_escape(name_esc, psys->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_pointcloud.c b/source/blender/makesrna/intern/rna_pointcloud.c index 075d660ba2b..4c5dcd5a587 100644 --- a/source/blender/makesrna/intern/rna_pointcloud.c +++ b/source/blender/makesrna/intern/rna_pointcloud.c @@ -27,18 +27,23 @@ # include "WM_api.h" # include "WM_types.h" -static PointCloud *rna_pointcloud(PointerRNA *ptr) +static PointCloud *rna_pointcloud(const PointerRNA *ptr) { return (PointCloud *)ptr->owner_id; } -static int rna_Point_index_get(PointerRNA *ptr) +static int rna_Point_index_get_const(const PointerRNA *ptr) { const PointCloud *pointcloud = rna_pointcloud(ptr); const float(*co)[3] = ptr->data; return (int)(co - pointcloud->co); } +static int rna_Point_index_get(PointerRNA *ptr) +{ + return rna_Point_index_get_const(ptr); +} + static void rna_Point_location_get(PointerRNA *ptr, float value[3]) { copy_v3_v3(value, (const float *)ptr->data); @@ -69,9 +74,9 @@ static void rna_Point_radius_set(PointerRNA *ptr, float value) pointcloud->radius[co - pointcloud->co] = value; } -static char *rna_Point_path(PointerRNA *ptr) +static char *rna_Point_path(const PointerRNA *ptr) { - return BLI_sprintfN("points[%d]", rna_Point_index_get(ptr)); + return BLI_sprintfN("points[%d]", rna_Point_index_get_const(ptr)); } static void rna_PointCloud_update_data(struct Main *UNUSED(bmain), diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index a80e9573657..4108baca2fa 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -108,14 +108,14 @@ static void rna_Pose_IK_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe BIK_clear_data(ob->pose); } -static char *rna_Pose_path(PointerRNA *UNUSED(ptr)) +static char *rna_Pose_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("pose"); } -static char *rna_PoseBone_path(PointerRNA *ptr) +static char *rna_PoseBone_path(const PointerRNA *ptr) { - bPoseChannel *pchan = ptr->data; + const bPoseChannel *pchan = ptr->data; char name_esc[sizeof(pchan->name) * 2]; BLI_str_escape(name_esc, pchan->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 997c110134d..11a7be69f68 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -483,9 +483,10 @@ static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, Point rna_iterator_listbase_begin(iter, &rl->passes, NULL); } -static int rna_RenderPass_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_RenderPass_rect_get_length(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - RenderPass *rpass = (RenderPass *)ptr->data; + const RenderPass *rpass = (RenderPass *)ptr->data; length[0] = rpass->rectx * rpass->recty; length[1] = rpass->channels; diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index c1012f67c5a..0c1fd8cab3c 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -148,7 +148,7 @@ static void rna_RigidBodyWorld_reset(Main *UNUSED(bmain), Scene *UNUSED(scene), BKE_rigidbody_cache_reset(rbw); } -static char *rna_RigidBodyWorld_path(PointerRNA *UNUSED(ptr)) +static char *rna_RigidBodyWorld_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("rigidbody_world"); } @@ -240,7 +240,7 @@ static void rna_RigidBodyOb_mesh_source_update(Main *bmain, Scene *scene, Pointe WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); } -static char *rna_RigidBodyOb_path(PointerRNA *UNUSED(ptr)) +static char *rna_RigidBodyOb_path(const PointerRNA *UNUSED(ptr)) { /* NOTE: this hardcoded path should work as long as only Objects have this */ return BLI_strdup("rigid_body"); @@ -432,7 +432,7 @@ static void rna_RigidBodyOb_angular_damping_set(PointerRNA *ptr, float value) # endif } -static char *rna_RigidBodyCon_path(PointerRNA *UNUSED(ptr)) +static char *rna_RigidBodyCon_path(const PointerRNA *UNUSED(ptr)) { /* NOTE: this hardcoded path should work as long as only Objects have this */ return BLI_strdup("rigid_body_constraint"); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index d540b51a585..ae67de9228c 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -721,7 +721,7 @@ static int rna_IntProperty_default_get(PointerRNA *ptr) return ((IntPropertyRNA *)prop)->defaultvalue; } /* int/float/bool */ -static int rna_NumberProperty_default_array_get_length(PointerRNA *ptr, +static int rna_NumberProperty_default_array_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { PropertyRNA *prop = (PropertyRNA *)ptr->data; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 63a68d04a97..0e423b71f1d 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1100,12 +1100,12 @@ static void rna_Scene_all_keyingsets_next(CollectionPropertyIterator *iter) iter->valid = (internal->link != NULL); } -static char *rna_SceneEEVEE_path(PointerRNA *UNUSED(ptr)) +static char *rna_SceneEEVEE_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("eevee"); } -static char *rna_SceneGpencil_path(PointerRNA *UNUSED(ptr)) +static char *rna_SceneGpencil_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("grease_pencil_settings"); } @@ -1129,17 +1129,17 @@ static void rna_RenderSettings_stereoViews_begin(CollectionPropertyIterator *ite rna_iterator_listbase_begin(iter, &rd->views, rna_RenderSettings_stereoViews_skip); } -static char *rna_RenderSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_RenderSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render"); } -static char *rna_BakeSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_BakeSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render.bake"); } -static char *rna_ImageFormatSettings_path(PointerRNA *ptr) +static char *rna_ImageFormatSettings_path(const PointerRNA *ptr) { ImageFormatData *imf = (ImageFormatData *)ptr->data; ID *id = ptr->owner_id; @@ -1787,10 +1787,11 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr) rna_Scene_glsl_update(bmain, activescene, ptr); } -static char *rna_ViewLayerEEVEE_path(PointerRNA *ptr) +static char *rna_ViewLayerEEVEE_path(const PointerRNA *ptr) { - ViewLayerEEVEE *view_layer_eevee = (ViewLayerEEVEE *)ptr->data; - ViewLayer *view_layer = (ViewLayer *)((uint8_t *)view_layer_eevee - offsetof(ViewLayer, eevee)); + const ViewLayerEEVEE *view_layer_eevee = (ViewLayerEEVEE *)ptr->data; + const ViewLayer *view_layer = (ViewLayer *)((uint8_t *)view_layer_eevee - + offsetof(ViewLayer, eevee)); char rna_path[sizeof(view_layer->name) * 3]; const size_t view_layer_path_len = rna_ViewLayer_path_buffer_get( @@ -1801,9 +1802,9 @@ static char *rna_ViewLayerEEVEE_path(PointerRNA *ptr) return BLI_strdup(rna_path); } -static char *rna_SceneRenderView_path(PointerRNA *ptr) +static char *rna_SceneRenderView_path(const PointerRNA *ptr) { - SceneRenderView *srv = (SceneRenderView *)ptr->data; + const SceneRenderView *srv = (SceneRenderView *)ptr->data; char srv_name_esc[sizeof(srv->name) * 2]; BLI_str_escape(srv_name_esc, srv->name, sizeof(srv_name_esc)); return BLI_sprintfN("render.views[\"%s\"]", srv_name_esc); @@ -2071,10 +2072,10 @@ static void rna_View3DCursor_matrix_set(PointerRNA *ptr, const float *values) BKE_scene_cursor_from_mat4(cursor, unit_mat, false); } -static char *rna_TransformOrientationSlot_path(PointerRNA *ptr) +static char *rna_TransformOrientationSlot_path(const PointerRNA *ptr) { - Scene *scene = (Scene *)ptr->owner_id; - TransformOrientationSlot *orientation_slot = ptr->data; + const Scene *scene = (Scene *)ptr->owner_id; + const TransformOrientationSlot *orientation_slot = ptr->data; if (!ELEM(NULL, scene, orientation_slot)) { for (int i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) { @@ -2089,7 +2090,7 @@ static char *rna_TransformOrientationSlot_path(PointerRNA *ptr) return BLI_strdup("transform_orientation_slots[0]"); } -static char *rna_View3DCursor_path(PointerRNA *UNUSED(ptr)) +static char *rna_View3DCursor_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("cursor"); } @@ -2188,17 +2189,17 @@ static void rna_UnifiedPaintSettings_radius_update(bContext *C, PointerRNA *ptr) rna_UnifiedPaintSettings_update(C, ptr); } -static char *rna_UnifiedPaintSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_UnifiedPaintSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.unified_paint_settings"); } -static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_CurvePaintSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.curve_paint_settings"); } -static char *rna_SequencerToolSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_SequencerToolSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.sequencer_tool_settings"); } @@ -2222,7 +2223,7 @@ static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr)) } } -static char *rna_MeshStatVis_path(PointerRNA *UNUSED(ptr)) +static char *rna_MeshStatVis_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.statvis"); } @@ -2260,7 +2261,7 @@ static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *UNUSED(scene), SEQ_cache_cleanup((Scene *)ptr->owner_id); } -static char *rna_ToolSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ToolSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings"); } @@ -2701,12 +2702,12 @@ static void rna_UnitSettings_system_update(Main *UNUSED(bmain), } } -static char *rna_UnitSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_UnitSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("unit_settings"); } -static char *rna_FFmpegSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_FFmpegSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render.ffmpeg"); } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 017e8bde7a1..ab5e4c2f0d5 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -259,7 +259,7 @@ static bool rna_ParticleEdit_hair_get(PointerRNA *ptr) return 0; } -static char *rna_ParticleEdit_path(PointerRNA *UNUSED(ptr)) +static char *rna_ParticleEdit_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.particle_edit"); } @@ -403,15 +403,15 @@ static void rna_Sculpt_ShowMask_update(bContext *C, PointerRNA *UNUSED(ptr)) WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, object); } -static char *rna_Sculpt_path(PointerRNA *UNUSED(ptr)) +static char *rna_Sculpt_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.sculpt"); } -static char *rna_VertexPaint_path(PointerRNA *ptr) +static char *rna_VertexPaint_path(const PointerRNA *ptr) { - Scene *scene = (Scene *)ptr->owner_id; - ToolSettings *ts = scene->toolsettings; + const Scene *scene = (Scene *)ptr->owner_id; + const ToolSettings *ts = scene->toolsettings; if (ptr->data == ts->vpaint) { return BLI_strdup("tool_settings.vertex_paint"); } @@ -420,47 +420,47 @@ static char *rna_VertexPaint_path(PointerRNA *ptr) } } -static char *rna_ImagePaintSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ImagePaintSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.image_paint"); } -static char *rna_PaintModeSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_PaintModeSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.paint_mode"); } -static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr)) +static char *rna_UvSculpt_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.uv_sculpt"); } -static char *rna_CurvesSculpt_path(PointerRNA *UNUSED(ptr)) +static char *rna_CurvesSculpt_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.curves_sculpt"); } -static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_paint"); } -static char *rna_GpVertexPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpVertexPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_vertex_paint"); } -static char *rna_GpSculptPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpSculptPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt_paint"); } -static char *rna_GpWeightPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpWeightPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_weight_paint"); } -static char *rna_ParticleBrush_path(PointerRNA *UNUSED(ptr)) +static char *rna_ParticleBrush_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.particle_edit.brush"); } @@ -578,12 +578,12 @@ static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint) return imapaint->missing_data == 0; } -static char *rna_GPencilSculptSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_GPencilSculptSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt"); } -static char *rna_GPencilSculptGuide_path(PointerRNA *UNUSED(ptr)) +static char *rna_GPencilSculptGuide_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt.guide"); } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 1e90d6f75b5..5d583d3edcb 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -504,7 +504,7 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor return data.seq; } -static char *rna_SequenceTransform_path(PointerRNA *ptr) +static char *rna_SequenceTransform_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); @@ -556,7 +556,7 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop) return data.seq; } -static char *rna_SequenceCrop_path(PointerRNA *ptr) +static char *rna_SequenceCrop_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); @@ -700,9 +700,9 @@ static StructRNA *rna_Sequence_refine(struct PointerRNA *ptr) } } -static char *rna_Sequence_path(PointerRNA *ptr) +static char *rna_Sequence_path(const PointerRNA *ptr) { - Sequence *seq = (Sequence *)ptr->data; + const Sequence *seq = (Sequence *)ptr->data; /* sequencer data comes from scene... * TODO: would be nice to make SequenceEditor data a data-block of its own (for shorter paths) @@ -1034,7 +1034,7 @@ static Sequence *sequence_get_by_colorbalance(Editing *ed, return data.seq; } -static char *rna_SequenceColorBalance_path(PointerRNA *ptr) +static char *rna_SequenceColorBalance_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; SequenceModifierData *smd; @@ -1178,7 +1178,7 @@ static StructRNA *rna_SequenceModifier_refine(struct PointerRNA *ptr) } } -static char *rna_SequenceModifier_path(PointerRNA *ptr) +static char *rna_SequenceModifier_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); @@ -1396,7 +1396,7 @@ static void rna_SequenceTimelineChannel_name_set(PointerRNA *ptr, const char *va sizeof(channel->name)); } -static char *rna_SeqTimelineChannel_path(PointerRNA *ptr) +static char *rna_SeqTimelineChannel_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c index cefa445740d..0a656222fd5 100644 --- a/source/blender/makesrna/intern/rna_shader_fx.c +++ b/source/blender/makesrna/intern/rna_shader_fx.c @@ -142,9 +142,9 @@ static void rna_ShaderFx_name_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(NULL, "shader_effects", oldname, gmd->name); } -static char *rna_ShaderFx_path(PointerRNA *ptr) +static char *rna_ShaderFx_path(const PointerRNA *ptr) { - ShaderFxData *gmd = ptr->data; + const ShaderFxData *gmd = ptr->data; char name_esc[sizeof(gmd->name) * 2]; BLI_str_escape(name_esc, gmd->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index de03e971b8b..4b5802d70ab 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1586,7 +1586,7 @@ static int rna_SpaceView3D_icon_from_show_object_viewport_get(PointerRNA *ptr) &v3d->object_type_exclude_select); } -static char *rna_View3DShading_path(PointerRNA *UNUSED(ptr)) +static char *rna_View3DShading_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("shading"); } @@ -1596,7 +1596,7 @@ static PointerRNA rna_SpaceView3D_overlay_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_View3DOverlay, ptr->data); } -static char *rna_View3DOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_View3DOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("overlay"); } @@ -1608,12 +1608,12 @@ static PointerRNA rna_SpaceImage_overlay_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_SpaceImageOverlay, ptr->data); } -static char *rna_SpaceImageOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceImageOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("overlay"); } -static char *rna_SpaceUVEditor_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceUVEditor_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("uv_editor"); } @@ -2045,7 +2045,7 @@ static void rna_SpaceProperties_context_update(Main *UNUSED(bmain), } } -static int rna_SpaceProperties_tab_search_results_getlength(PointerRNA *ptr, +static int rna_SpaceProperties_tab_search_results_getlength(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { SpaceProperties *sbuts = ptr->data; @@ -2447,12 +2447,12 @@ static void rna_Sequencer_view_type_update(Main *UNUSED(bmain), ED_area_tag_refresh(area); } -static char *rna_SpaceSequencerPreviewOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceSequencerPreviewOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("preview_overlay"); } -static char *rna_SpaceSequencerTimelineOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceSequencerTimelineOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("timeline_overlay"); } @@ -2463,7 +2463,7 @@ static PointerRNA rna_SpaceNode_overlay_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_SpaceNodeOverlay, ptr->data); } -static char *rna_SpaceNodeOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceNodeOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("overlay"); } @@ -2656,7 +2656,7 @@ static void rna_SpaceClipEditor_view_type_update(Main *UNUSED(bmain), /* File browser. */ -static char *rna_FileSelectParams_path(PointerRNA *UNUSED(ptr)) +static char *rna_FileSelectParams_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("params"); } diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 6f7ee966723..eff7c0ca136 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -291,7 +291,7 @@ void rna_TextureSlot_update(bContext *C, PointerRNA *ptr) } } -char *rna_TextureSlot_path(PointerRNA *ptr) +char *rna_TextureSlot_path(const PointerRNA *ptr) { MTex *mtex = ptr->data; diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index aac84f1867e..b9acd57430b 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -41,7 +41,7 @@ # include "WM_api.h" -static char *rna_tracking_path(PointerRNA *UNUSED(ptr)) +static char *rna_tracking_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tracking"); } @@ -72,7 +72,7 @@ static void rna_tracking_defaultSettings_searchUpdate(Main *UNUSED(bmain), } } -static char *rna_trackingTrack_path(PointerRNA *ptr) +static char *rna_trackingTrack_path(const PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->owner_id; MovieTrackingTrack *track = (MovieTrackingTrack *)ptr->data; @@ -255,7 +255,7 @@ static void rna_trackingPlaneMarker_frame_set(PointerRNA *ptr, int value) } } -static char *rna_trackingPlaneTrack_path(PointerRNA *ptr) +static char *rna_trackingPlaneTrack_path(const PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->owner_id; MovieTrackingPlaneTrack *plane_track = (MovieTrackingPlaneTrack *)ptr->data; @@ -289,7 +289,7 @@ static void rna_trackingPlaneTrack_name_set(PointerRNA *ptr, const char *value) } } -static char *rna_trackingCamera_path(PointerRNA *UNUSED(ptr)) +static char *rna_trackingCamera_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tracking.camera"); } @@ -321,7 +321,7 @@ static void rna_trackingCamera_focal_mm_set(PointerRNA *ptr, float value) } } -static char *rna_trackingStabilization_path(PointerRNA *UNUSED(ptr)) +static char *rna_trackingStabilization_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tracking.stabilization"); } diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index 12cb35b239d..6d4ea18fc38 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -46,12 +46,12 @@ const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { # include "WM_api.h" # include "WM_types.h" -static char *rna_VolumeRender_path(PointerRNA *UNUSED(ptr)) +static char *rna_VolumeRender_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render"); } -static char *rna_VolumeDisplay_path(PointerRNA *UNUSED(ptr)) +static char *rna_VolumeDisplay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("display"); } @@ -485,6 +485,21 @@ static void rna_def_volume_render(BlenderRNA *brna) RNA_def_struct_sdna(srna, "VolumeRender"); RNA_def_struct_path_func(srna, "rna_VolumeRender_path"); + static const EnumPropertyItem precision_items[] = { + {VOLUME_PRECISION_FULL, "FULL", 0, "Full", "Full float (Use 32 bit for all data)"}, + {VOLUME_PRECISION_HALF, "HALF", 0, "Half", "Half float (Use 16 bit for all data)"}, + {VOLUME_PRECISION_VARIABLE, "VARIABLE", 0, "Variable", "Use variable bit quantization"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "precision", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, precision_items); + RNA_def_property_ui_text(prop, + "Precision", + "Specify volume data precision. Lower values reduce memory consumption " + "at the cost of detail"); + RNA_def_property_update(prop, 0, "rna_Volume_update_display"); + static const EnumPropertyItem space_items[] = { {VOLUME_SPACE_OBJECT, "OBJECT", diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index ac1b7e53a9b..c80d0e2da39 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2599,6 +2599,9 @@ static void rna_def_keyconfig(BlenderRNA *brna) "rna_wmKeyMapItem_idname_get", "rna_wmKeyMapItem_idname_length", "rna_wmKeyMapItem_idname_set"); + RNA_def_property_string_search_func(prop, + "WM_operatortype_idname_visit_for_search", + PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION); RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 4fbf5192222..2917861f084 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -89,8 +89,9 @@ class GFieldValueLog : public ValueLog { struct GeometryAttributeInfo { std::string name; - AttributeDomain domain; - CustomDataType data_type; + /** Can be empty when #name does not actually exist on a geometry yet. */ + std::optional<AttributeDomain> domain; + std::optional<CustomDataType> data_type; }; /** Contains information about a geometry set. In most cases this does not store the entire diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7dd732e7fad..e0a4d241b3b 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -44,7 +44,7 @@ DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZ DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" ) DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" ) DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "" ) -DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) +DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" ) DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "" ) @@ -216,7 +216,7 @@ DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOIS DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" ) DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" ) DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) -DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" ) +DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space", "" ) DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBINE_XYZ", CombineXYZ, "Combine XYZ", "" ) DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" ) @@ -234,7 +234,7 @@ DefNode(TextureNode, TEX_NODE_VALTORGB, def_colorramp, "VALTOR DefNode(TextureNode, TEX_NODE_IMAGE, def_tex_image, "IMAGE", Image, "Image", "" ) DefNode(TextureNode, TEX_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", CurveRGB, "RGB Curves", "" ) DefNode(TextureNode, TEX_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" ) -DefNode(TextureNode, TEX_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) +DefNode(TextureNode, TEX_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value", "" ) DefNode(TextureNode, TEX_NODE_CURVE_TIME, def_time, "CURVE_TIME", CurveTime, "Curve Time", "" ) DefNode(TextureNode, TEX_NODE_ROTATE, 0, "ROTATE", Rotate, "Rotate", "" ) DefNode(TextureNode, TEX_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "" ) diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc index 9b88e99060c..e52c6d096b9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc @@ -18,9 +18,9 @@ static void cmp_node_map_range_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("From Max")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("To Max")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); b.add_output<decl::Float>(N_("Value")); } diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc index 7bc9d2d79e8..e3f13dc7d6b 100644 --- a/source/blender/nodes/function/nodes/node_fn_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_compare.cc @@ -487,7 +487,7 @@ static const fn::MultiFunction *get_multi_function(bNode &node) static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Not Equal - Element-wise", [](float3 a, float3 b, float epsilon) { - return abs(a.x - b.x) > epsilon && abs(a.y - b.y) > epsilon && + return abs(a.x - b.x) > epsilon || abs(a.y - b.y) > epsilon || abs(a.z - b.z) > epsilon; }, exec_preset_first_two}; @@ -522,7 +522,7 @@ static const fn::MultiFunction *get_multi_function(bNode &node) static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{ "Not Equal", [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { - return abs(a.r - b.r) > epsilon && abs(a.g - b.g) > epsilon && + return abs(a.r - b.r) > epsilon || abs(a.g - b.g) > epsilon || abs(a.b - b.b) > epsilon; }, exec_preset_first_two}; diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc index 6abcc79cf29..4b493438a45 100644 --- a/source/blender/nodes/function/nodes/node_fn_replace_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -15,19 +15,17 @@ static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) b.add_output<decl::String>(N_("String")); } -static std::string replace_all(std::string str, const std::string &from, const std::string &to) +static std::string replace_all(const StringRefNull str, + const StringRefNull from, + const StringRefNull to) { - if (from.length() <= 0) { + if (from.is_empty()) { return str; } - const size_t step = to.length() > 0 ? to.length() : 1; - - size_t offset = 0; - while ((offset = str.find(from, offset)) != std::string::npos) { - str.replace(offset, from.length(), to); - offset += step; - } - return str; + char *new_str_ptr = BLI_str_replaceN(str.c_str(), from.c_str(), to.c_str()); + std::string new_str{new_str_ptr}; + MEM_freeN(new_str_ptr); + return new_str; } static void fn_node_replace_string_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 18cf005c965..16967d32673 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -126,30 +126,66 @@ static void try_capture_field_on_geometry(GeometryComponent &component, output_attribute.save(); } +static StringRefNull identifier_suffix(CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return "_001"; + case CD_PROP_INT32: + return "_004"; + case CD_PROP_COLOR: + return "_002"; + case CD_PROP_BOOL: + return "_003"; + case CD_PROP_FLOAT3: + return ""; + default: + BLI_assert_unreachable(); + return ""; + } +} + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + if (!params.output_is_required("Geometry")) { + params.error_message_add( + NodeWarningType::Info, + TIP_("The attribute output can not be used without the geometry output")); + params.set_default_remaining_outputs(); + return; + } + const NodeGeometryAttributeCapture &storage = node_storage(params.node()); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + const std::string output_identifier = "Attribute" + identifier_suffix(data_type); + + if (!params.output_is_required(output_identifier)) { + params.set_output("Geometry", geometry_set); + return; + } + + const std::string input_identifier = "Value" + identifier_suffix(data_type); GField field; + switch (data_type) { case CD_PROP_FLOAT: - field = params.get_input<Field<float>>("Value_001"); + field = params.get_input<Field<float>>(input_identifier); break; case CD_PROP_FLOAT3: - field = params.get_input<Field<float3>>("Value"); + field = params.get_input<Field<float3>>(input_identifier); break; case CD_PROP_COLOR: - field = params.get_input<Field<ColorGeometry4f>>("Value_002"); + field = params.get_input<Field<ColorGeometry4f>>(input_identifier); break; case CD_PROP_BOOL: - field = params.get_input<Field<bool>>("Value_003"); + field = params.get_input<Field<bool>>(input_identifier); break; case CD_PROP_INT32: - field = params.get_input<Field<int>>("Value_004"); + field = params.get_input<Field<int>>(input_identifier); break; default: break; @@ -185,23 +221,23 @@ static void node_geo_exec(GeoNodeExecParams params) switch (data_type) { case CD_PROP_FLOAT: { - params.set_output("Attribute_001", Field<float>(output_field)); + params.set_output(output_identifier, Field<float>(output_field)); break; } case CD_PROP_FLOAT3: { - params.set_output("Attribute", Field<float3>(output_field)); + params.set_output(output_identifier, Field<float3>(output_field)); break; } case CD_PROP_COLOR: { - params.set_output("Attribute_002", Field<ColorGeometry4f>(output_field)); + params.set_output(output_identifier, Field<ColorGeometry4f>(output_field)); break; } case CD_PROP_BOOL: { - params.set_output("Attribute_003", Field<bool>(output_field)); + params.set_output(output_identifier, Field<bool>(output_field)); break; } case CD_PROP_INT32: { - params.set_output("Attribute_004", Field<int>(output_field)); + params.set_output(output_identifier, Field<int>(output_field)); break; } default: diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index d9cc8bcf023..78a132064ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -73,7 +73,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - Field<int> length = params.extract_input<Field<float>>("Length"); + Field<float> length = params.extract_input<Field<float>>("Length"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { if (!component->is_empty()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 658de02fdab..33f5eccd719 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -97,12 +97,8 @@ static void node_update(bNodeTree *ntree, bNode *node) ntree, socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last; - bNodeSocket *width_socket = height_socket->prev; nodeSetSocketAvailability( ntree, height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); - node_sock_label(width_socket, - overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") : - N_("Text Box Width")); } static float3 get_pivot_point(GeoNodeExecParams ¶ms, CurveEval &curve) diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index c5f40a46ca3..f107ec73c60 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -587,10 +587,11 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, } } } - /* Recreate links between copied nodes. */ + /* Recreate links between copied nodes AND incoming links to the copied nodes. */ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (link->fromnode->tmp_flag >= 0 && link->tonode->tmp_flag >= 0) { - bNode *fromnode = nodes_copy[link->fromnode->tmp_flag]; + if (link->tonode->tmp_flag >= 0) { + bool from_node_copied = link->fromnode->tmp_flag >= 0; + bNode *fromnode = from_node_copied ? nodes_copy[link->fromnode->tmp_flag] : link->fromnode; bNode *tonode = nodes_copy[link->tonode->tmp_flag]; bNodeSocket *fromsock = ntree_shader_node_find_output(fromnode, link->fromsock->identifier); bNodeSocket *tosock = ntree_shader_node_find_input(tonode, link->tosock->identifier); diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index c47b69e6b69..059d7800fc5 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -329,7 +329,7 @@ void node_shader_gpu_tex_mapping(GPUMaterial *mat, void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data) { - const float *xyz_to_rgb = IMB_colormanagement_get_xyz_to_rgb(); + const float *xyz_to_rgb = IMB_colormanagement_get_xyz_to_scene_linear(); data->r[0] = xyz_to_rgb[0]; data->r[1] = xyz_to_rgb[3]; data->r[2] = xyz_to_rgb[6]; diff --git a/source/blender/nodes/shader/nodes/node_shader_blackbody.cc b/source/blender/nodes/shader/nodes/node_shader_blackbody.cc index 62e631efb57..9f382e5a3bb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_blackbody.cc +++ b/source/blender/nodes/shader/nodes/node_shader_blackbody.cc @@ -3,6 +3,8 @@ #include "node_shader_util.hh" +#include "IMB_colormanagement.h" + namespace blender::nodes::node_shader_blackbody_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -20,7 +22,7 @@ static int node_shader_gpu_blackbody(GPUMaterial *mat, const int size = CM_TABLE + 1; float *data = static_cast<float *>(MEM_mallocN(sizeof(float) * size * 4, "blackbody texture")); - blackbody_temperature_to_rgb_table(data, size, 965.0f, 12000.0f); + IMB_colormanagement_blackbody_temperature_to_rgb_table(data, size, 800.0f, 12000.0f); float layer; GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer); diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc index 07e700e550a..4c4122a905f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc @@ -3,6 +3,8 @@ #include "node_shader_util.hh" +#include "IMB_colormanagement.h" + namespace blender::nodes::node_shader_volume_principled_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -109,7 +111,7 @@ static int node_shader_gpu_volume_principled(GPUMaterial *mat, float *data, layer; if (use_blackbody) { data = (float *)MEM_mallocN(sizeof(float) * size * 4, "blackbody texture"); - blackbody_temperature_to_rgb_table(data, size, 965.0f, 12000.0f); + IMB_colormanagement_blackbody_temperature_to_rgb_table(data, size, 800.0f, 12000.0f); } else { data = (float *)MEM_callocN(sizeof(float) * size * 4, "blackbody black"); diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.cc b/source/blender/nodes/shader/nodes/node_shader_wavelength.cc index 522928a30fa..34fa639dd07 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wavelength.cc +++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.cc @@ -3,6 +3,8 @@ #include "node_shader_util.hh" +#include "IMB_colormanagement.h" + namespace blender::nodes::node_shader_wavelength_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -20,22 +22,11 @@ static int node_shader_gpu_wavelength(GPUMaterial *mat, const int size = CM_TABLE + 1; float *data = static_cast<float *>(MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture")); - wavelength_to_xyz_table(data, size); + IMB_colormanagement_wavelength_to_rgb_table(data, size); float layer; GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer); - XYZ_to_RGB xyz_to_rgb; - get_XYZ_to_RGB_for_gpu(&xyz_to_rgb); - return GPU_stack_link(mat, - node, - "node_wavelength", - in, - out, - ramp_texture, - GPU_constant(&layer), - GPU_uniform(xyz_to_rgb.r), - GPU_uniform(xyz_to_rgb.g), - GPU_uniform(xyz_to_rgb.b)); + return GPU_stack_link(mat, node, "node_wavelength", in, out, ramp_texture, GPU_constant(&layer)); } } // namespace blender::nodes::node_shader_wavelength_cc diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c index 04975fcef96..616e7da6e9f 100644 --- a/source/blender/python/gpu/gpu_py_element.c +++ b/source/blender/python/gpu/gpu_py_element.c @@ -91,15 +91,11 @@ static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject *UNUSED(type), PyObject *ar /* Use `INT_MAX` instead of the actual number of vertices. */ GPU_indexbuf_init(&builder, prim_type.value_found, index_len, INT_MAX); -#if 0 uint *buf = pybuffer.buf; for (uint i = index_len; i--; buf++) { GPU_indexbuf_add_generic_vert(&builder, *buf); } -#else - memcpy(builder.data, pybuffer.buf, index_len * sizeof(*builder.data)); - builder.index_len = index_len; -#endif + PyBuffer_Release(&pybuffer); } else { diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 80c48e31510..d7369731a98 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -752,28 +752,27 @@ static PyObject *pygpu_shader_unbind(BPyGPUShader *UNUSED(self)) Py_RETURN_NONE; } -PyDoc_STRVAR(pygpu_shader_from_builtin_doc, - ".. function:: from_builtin(shader_name, config='DEFAULT')\n" - "\n" - " Shaders that are embedded in the blender internal code:\n" - "" PYDOC_BUILTIN_SHADER_DESCRIPTION - "\n" - " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n" - " which can be edited by the :mod:`gpu.matrix` module.\n" - "\n" - " You can also choose a shader configuration that uses clip_planes by setting the " - "``CLIPPED`` value to the config parameter. Note that in this case you also need to " - "manually set the value of ``mat4 ModelMatrix``.\n" - "\n" - " :param shader_name: One of the builtin shader names.\n" - " :type shader_name: str\n" - " :param config: One of these types of shader configuration:\n" - "\n" - " - ``DEFAULT``\n" - " - ``CLIPPED``\n" - " :type config: str\n" - " :return: Shader object corresponding to the given name.\n" - " :rtype: :class:`bpy.types.GPUShader`\n"); +PyDoc_STRVAR( + pygpu_shader_from_builtin_doc, + ".. function:: from_builtin(shader_name, config='DEFAULT')\n" + "\n" + " Shaders that are embedded in the blender internal code (see :ref:`built-in-shaders`).\n" + " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n" + " which can be edited by the :mod:`gpu.matrix` module.\n" + "\n" + " You can also choose a shader configuration that uses clip_planes by setting the " + "``CLIPPED`` value to the config parameter. Note that in this case you also need to " + "manually set the value of ``mat4 ModelMatrix``.\n" + "\n" + " :param shader_name: One of the builtin shader names.\n" + " :type shader_name: str\n" + " :param config: One of these types of shader configuration:\n" + "\n" + " - ``DEFAULT``\n" + " - ``CLIPPED``\n" + " :type config: str\n" + " :return: Shader object corresponding to the given name.\n" + " :rtype: :class:`bpy.types.GPUShader`\n"); static PyObject *pygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) { BPYGPU_IS_INIT_OR_ERROR_OBJ; @@ -854,6 +853,8 @@ static struct PyMethodDef pygpu_shader_module__tp_methods[] = { PyDoc_STRVAR(pygpu_shader_module__tp_doc, "This module provides access to GPUShader internal functions.\n" "\n" + ".. _built-in-shaders:\n" + "\n" ".. rubric:: Built-in shaders\n" "\n" "All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.\n" diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index a6aa1f46b0c..1ee778ae801 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -191,6 +191,17 @@ static const EnumPropertyItem property_subtype_array_items[] = { "'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \ " :type subtype: string\n" +static const EnumPropertyItem property_string_search_options_items[] = { + {PROP_STRING_SEARCH_SORT, "SORT", 0, "Sort Search Results", ""}, + {PROP_STRING_SEARCH_SUGGESTION, + "SUGGESTION", + 0, + "Suggestion", + "Search results are suggestions (other values may be entered)"}, + + {0, NULL, 0, NULL, NULL}, +}; + /** \} */ /* -------------------------------------------------------------------- */ @@ -257,6 +268,11 @@ struct BPyPropStore { /** Wrap: #RNA_def_property_poll_runtime */ PyObject *poll_fn; } pointer_data; + /** #PROP_STRING type. */ + struct { + /** Wrap: #RNA_def_property_string_search_func_runtime */ + PyObject *search_fn; + } string_data; }; } py_data; }; @@ -1672,6 +1688,163 @@ static void bpy_prop_string_set_fn(struct PointerRNA *ptr, } } +static bool bpy_prop_string_visit_fn_call(PyObject *py_func, + PyObject *item, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + const char *text; + const char *info = NULL; + + if (PyTuple_CheckExact(item)) { + /* Positional only. */ + static const char *_keywords[] = { + "", + "", + NULL, + }; + static _PyArg_Parser _parser = { + "s" /* `text` */ + "s" /* `info` */ + ":search", + _keywords, + 0, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(item, NULL, &_parser, &text, &info)) { + PyC_Err_PrintWithFunc(py_func); + return false; + } + } + else { + text = PyUnicode_AsUTF8(item); + if (UNLIKELY(text == NULL)) { + PyErr_Clear(); + PyErr_Format(PyExc_TypeError, + "expected sequence of strings or tuple pairs of strings, not %.200s", + Py_TYPE(item)->tp_name); + PyC_Err_PrintWithFunc(py_func); + return false; + } + } + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = text; + visit_params.info = info; + visit_fn(visit_user_data, &visit_params); + return true; +} + +static void bpy_prop_string_visit_for_search_fn(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + struct BPyPropStore *prop_store = RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + PyGILState_STATE gilstate; + PyObject *py_edit_text; + + BLI_assert(prop_store != NULL); + + if (C) { + bpy_context_set((struct bContext *)C, &gilstate); + } + else { + gilstate = PyGILState_Ensure(); + } + + py_func = prop_store->py_data.string_data.search_fn; + + args = PyTuple_New(3); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + Py_INCREF(bpy_context_module); + PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module); + + py_edit_text = PyUnicode_FromString(edit_text); + PyTuple_SET_ITEM(args, 2, py_edit_text); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + PyC_Err_PrintWithFunc(py_func); + } + else { + if (PyIter_Check(ret)) { + /* Iterators / generator types. */ + PyObject *it; + PyObject *(*iternext)(PyObject *); + it = PyObject_GetIter(ret); + if (it == NULL) { + PyC_Err_PrintWithFunc(py_func); + } + else { + iternext = *Py_TYPE(it)->tp_iternext; + for (;;) { + PyObject *py_text = iternext(it); + if (py_text == NULL) { + break; + } + const bool ok = bpy_prop_string_visit_fn_call( + py_func, py_text, visit_fn, visit_user_data); + Py_DECREF(py_text); + if (!ok) { + break; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + } + else { + PyC_Err_PrintWithFunc(py_func); + } + } + } + } + else { + /* Sequence (typically list/tuple). */ + PyObject *ret_fast = PySequence_Fast( + ret, + "StringProperty(...): " + "return value from search callback was not a sequence, iterator or generator"); + if (ret_fast == NULL) { + PyC_Err_PrintWithFunc(py_func); + } + else { + const Py_ssize_t ret_num = PySequence_Fast_GET_SIZE(ret_fast); + PyObject **ret_fast_items = PySequence_Fast_ITEMS(ret_fast); + for (Py_ssize_t i = 0; i < ret_num; i++) { + const bool ok = bpy_prop_string_visit_fn_call( + py_func, ret_fast_items[i], visit_fn, visit_user_data); + if (!ok) { + break; + } + } + Py_DECREF(ret_fast); + } + } + + Py_DECREF(ret); + } + + if (C) { + bpy_context_clear((struct bContext *)C, &gilstate); + } + else { + PyGILState_Release(gilstate); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2352,11 +2525,14 @@ static void bpy_prop_callback_assign_float_array(struct PropertyRNA *prop, static void bpy_prop_callback_assign_string(struct PropertyRNA *prop, PyObject *get_fn, - PyObject *set_fn) + PyObject *set_fn, + PyObject *search_fn, + const eStringPropertySearchFlag search_flag) { StringPropertyGetFunc rna_get_fn = NULL; StringPropertyLengthFunc rna_length_fn = NULL; StringPropertySetFunc rna_set_fn = NULL; + StringPropertySearchFunc rna_search_fn = NULL; if (get_fn && get_fn != Py_None) { struct BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop); @@ -2372,8 +2548,17 @@ static void bpy_prop_callback_assign_string(struct PropertyRNA *prop, rna_set_fn = bpy_prop_string_set_fn; ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn); } + if (search_fn) { + struct BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop); + + rna_search_fn = bpy_prop_string_visit_for_search_fn; + ASSIGN_PYOBJECT_INCREF(prop_store->py_data.string_data.search_fn, search_fn); + } RNA_def_property_string_funcs_runtime(prop, rna_get_fn, rna_length_fn, rna_set_fn); + if (rna_search_fn) { + RNA_def_property_string_search_func_runtime(prop, rna_search_fn, search_flag); + } } static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, @@ -2628,6 +2813,24 @@ static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p) " This function must take 2 values (self, value) and return None.\n" \ " :type set: function\n" +#define BPY_PROPDEF_SEARCH_DOC \ + " :arg search: Function to be called to show candidates for this string (shown in the UI).\n" \ + " This function must take 3 values (self, context, edit_text)\n" \ + " and return a sequence, iterator or generator where each item must be:\n" \ + "\n" \ + " - A single string (representing a candidate to display).\n" \ + " - A tuple-pair of strings, where the first is a candidate and the second\n" \ + " is additional information about the candidate.\n" \ + " :type search: function\n" \ + " :arg search_options: Set of strings in:\n" \ + "\n" \ + " - 'SORT' sorts the resulting items.\n" \ + " - 'SUGGESTION' lets the user enter values not found in search candidates.\n" \ + " **WARNING** disabling this flag causes the search callback to run on redraw,\n" \ + " so only disable this flag if it's not likely to cause performance issues.\n" \ + "\n" \ + " :type search_options: set\n" + #define BPY_PROPDEF_POINTER_TYPE_DOC \ " :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \ " :type type: class\n" @@ -3721,7 +3924,9 @@ PyDoc_STRVAR(BPy_StringProperty_doc, "subtype='NONE', " "update=None, " "get=None, " - "set=None)\n" + "set=None, " + "search=None, " + "search_options={'SUGGESTION'})\n" "\n" " Returns a new string property definition.\n" "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC @@ -3730,7 +3935,7 @@ PyDoc_STRVAR(BPy_StringProperty_doc, " :arg maxlen: maximum length of the string.\n" " :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC - BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC BPY_PROPDEF_SEARCH_DOC); static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3767,6 +3972,11 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw PyObject *update_fn = NULL; PyObject *get_fn = NULL; PyObject *set_fn = NULL; + PyObject *search_fn = NULL; + static struct BPy_EnumProperty_Parse search_options_enum = { + .items = property_string_search_options_items, + .value = PROP_STRING_SEARCH_SUGGESTION, + }; static const char *_keywords[] = { "attr", @@ -3781,6 +3991,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw "update", "get", "set", + "search", + "search_options", NULL, }; static _PyArg_Parser _parser = { @@ -3797,6 +4009,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw "O" /* `update` */ "O" /* `get` */ "O" /* `set` */ + "O" /* `search` */ + "O&" /* `search_options` */ ":StringProperty", _keywords, 0, @@ -3820,7 +4034,10 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw &subtype_enum, &update_fn, &get_fn, - &set_fn)) { + &set_fn, + &search_fn, + pyrna_enum_bitfield_parse_set, + &search_options_enum)) { return NULL; } @@ -3833,6 +4050,9 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { return NULL; } + if (bpy_prop_callback_check(search_fn, "search", 3) == -1) { + return NULL; + } if (id_data.prop_free_handle != NULL) { RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle); @@ -3858,7 +4078,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw bpy_prop_assign_flag_override(prop, override_enum.value); } bpy_prop_callback_assign_update(prop, update_fn); - bpy_prop_callback_assign_string(prop, get_fn, set_fn); + bpy_prop_callback_assign_string(prop, get_fn, set_fn, search_fn, search_options_enum.value); RNA_def_property_duplicate_pointers(srna, prop); Py_RETURN_NONE; diff --git a/source/blender/python/mathutils/CMakeLists.txt b/source/blender/python/mathutils/CMakeLists.txt index 1be9b568626..747d6c0e8f8 100644 --- a/source/blender/python/mathutils/CMakeLists.txt +++ b/source/blender/python/mathutils/CMakeLists.txt @@ -4,6 +4,7 @@ set(INC . ../../blenkernel ../../blenlib + ../../imbuf ../../bmesh ../../depsgraph ../../makesdna @@ -42,6 +43,7 @@ set(SRC set(LIB bf_blenlib + bf_imbuf bf_python_ext ${PYTHON_LINKFLAGS} diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 8b126639f10..88e8d880360 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -14,14 +14,48 @@ #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" +#include "IMB_colormanagement.h" + #ifndef MATH_STANDALONE # include "BLI_dynstr.h" #endif #define COLOR_SIZE 3 -/* ----------------------------------mathutils.Color() ------------------- */ -/* makes a new color for you to play with */ +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +/** + * \note #BaseMath_ReadCallback must be called beforehand. + */ +static PyObject *Color_to_tuple_ex(ColorObject *self, int ndigits) +{ + PyObject *ret; + int i; + + ret = PyTuple_New(COLOR_SIZE); + + if (ndigits >= 0) { + for (i = 0; i < COLOR_SIZE; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->col[i], ndigits))); + } + } + else { + for (i = 0; i < COLOR_SIZE; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->col[i])); + } + } + + return ret; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: `__new__` / `mathutils.Color()` + * \{ */ + static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { float col[3] = {0.0f, 0.0f, 0.0f}; @@ -52,30 +86,130 @@ static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return Color_CreatePyObject(col, type); } -/* -----------------------------METHODS---------------------------- */ +/** \} */ -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ -static PyObject *Color_ToTupleExt(ColorObject *self, int ndigits) +/* -------------------------------------------------------------------- */ +/** \name Color Methods: Color Space Conversion + * \{ */ + +PyDoc_STRVAR(Color_from_scene_linear_to_srgb_doc, + ".. function:: from_scene_linear_to_srgb()\n" + "\n" + " Convert from scene linear to sRGB color space.\n" + "\n" + " :return: A color in sRGB color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_scene_linear_to_srgb(ColorObject *self) { - PyObject *ret; - int i; + float col[3]; + IMB_colormanagement_scene_linear_to_srgb_v3(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} - ret = PyTuple_New(COLOR_SIZE); +PyDoc_STRVAR(Color_from_srgb_to_scene_linear_doc, + ".. function:: from_srgb_to_scene_linear()\n" + "\n" + " Convert from sRGB to scene linear color space.\n" + "\n" + " :return: A color in scene linear color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_srgb_to_scene_linear(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_srgb_to_scene_linear_v3(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} - if (ndigits >= 0) { - for (i = 0; i < COLOR_SIZE; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->col[i], ndigits))); - } - } - else { - for (i = 0; i < COLOR_SIZE; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->col[i])); - } - } +PyDoc_STRVAR(Color_from_scene_linear_to_xyz_d65_doc, + ".. function:: from_scene_linear_to_xyz_d65()\n" + "\n" + " Convert from scene linear to CIE XYZ (Illuminant D65) color space.\n" + "\n" + " :return: A color in XYZ color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_scene_linear_to_xyz_d65(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_scene_linear_to_xyz(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} - return ret; +PyDoc_STRVAR(Color_from_xyz_d65_to_scene_linear_doc, + ".. function:: from_xyz_d65_to_scene_linear()\n" + "\n" + " Convert from CIE XYZ (Illuminant D65) to scene linear color space.\n" + "\n" + " :return: A color in scene linear color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_xyz_d65_to_scene_linear(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_xyz_to_scene_linear(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} + +PyDoc_STRVAR(Color_from_scene_linear_to_aces_doc, + ".. function:: from_scene_linear_to_aces()\n" + "\n" + " Convert from scene linear to ACES2065-1 linear color space.\n" + "\n" + " :return: A color in ACES2065-1 linear color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_scene_linear_to_aces(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_scene_linear_to_aces(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} + +PyDoc_STRVAR(Color_from_aces_to_scene_linear_doc, + ".. function:: from_aces_to_scene_linear()\n" + "\n" + " Convert from ACES2065-1 linear to scene linear color space.\n" + "\n" + " :return: A color in scene linear color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_aces_to_scene_linear(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_aces_to_scene_linear(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); } +PyDoc_STRVAR(Color_from_scene_linear_to_rec709_linear_doc, + ".. function:: from_scene_linear_to_rec709_linear()\n" + "\n" + " Convert from scene linear to Rec.709 linear color space.\n" + "\n" + " :return: A color in Rec.709 linear color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_scene_linear_to_rec709_linear(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_scene_linear_to_rec709(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} + +PyDoc_STRVAR(Color_from_rec709_linear_to_scene_linear_doc, + ".. function:: from_rec709_linear_to_scene_linear()\n" + "\n" + " Convert from Rec.709 linear color space to scene linear color space.\n" + "\n" + " :return: A color in scene linear color space.\n" + " :rtype: :class:`Color`\n"); +static PyObject *Color_from_rec709_linear_to_scene_linear(ColorObject *self) +{ + float col[3]; + IMB_colormanagement_rec709_to_scene_linear(col, self->col); + return Color_CreatePyObject(col, Py_TYPE(self)); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Methods: Color Copy/Deep-Copy + * \{ */ + PyDoc_STRVAR(Color_copy_doc, ".. function:: copy()\n" "\n" @@ -102,8 +236,11 @@ static PyObject *Color_deepcopy(ColorObject *self, PyObject *args) return Color_copy(self); } -/* ----------------------------print object (internal)-------------- */ -/* print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: `__repr__` & `__str__` + * \{ */ static PyObject *Color_repr(ColorObject *self) { @@ -113,7 +250,7 @@ static PyObject *Color_repr(ColorObject *self) return NULL; } - tuple = Color_ToTupleExt(self, -1); + tuple = Color_to_tuple_ex(self, -1); ret = PyUnicode_FromFormat("Color(%R)", tuple); @@ -139,8 +276,12 @@ static PyObject *Color_str(ColorObject *self) } #endif -/* ------------------------tp_richcmpr */ -/* returns -1 exception, 0 false, 1 true */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Rich Compare + * \{ */ + static PyObject *Color_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -179,6 +320,12 @@ static PyObject *Color_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Color_hash(ColorObject *self) { if (BaseMath_ReadCallback(self) == -1) { @@ -192,15 +339,19 @@ static Py_hash_t Color_hash(ColorObject *self) return mathutils_array_hash(self->col, COLOR_SIZE); } -/* ---------------------SEQUENCE PROTOCOLS------------------------ */ -/* ----------------------------len(object)------------------------ */ -/* sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Sequence & Mapping Protocols Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Color_len(ColorObject *UNUSED(self)) { return COLOR_SIZE; } -/* ----------------------------object[]--------------------------- */ -/* sequence accessor (get) */ + +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Color_item(ColorObject *self, int i) { if (i < 0) { @@ -220,8 +371,8 @@ static PyObject *Color_item(ColorObject *self, int i) return PyFloat_FromDouble(self->col[i]); } -/* ----------------------------object[]------------------------- */ -/* sequence accessor (set) */ + +/** Sequence accessor (set): `object[i] = x`. */ static int Color_ass_item(ColorObject *self, int i, PyObject *value) { float f; @@ -257,8 +408,8 @@ static int Color_ass_item(ColorObject *self, int i, PyObject *value) return 0; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (get) */ + +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Color_slice(ColorObject *self, int begin, int end) { PyObject *tuple; @@ -282,8 +433,8 @@ static PyObject *Color_slice(ColorObject *self, int begin, int end) return tuple; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (set) */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Color_ass_slice(ColorObject *self, int begin, int end, PyObject *seq) { int i, size; @@ -320,6 +471,7 @@ static int Color_ass_slice(ColorObject *self, int begin, int end, PyObject *seq) return 0; } +/** Sequence generic subscript (get): `x = object[...]`. */ static PyObject *Color_subscript(ColorObject *self, PyObject *item) { if (PyIndex_Check(item)) { @@ -356,6 +508,7 @@ static PyObject *Color_subscript(ColorObject *self, PyObject *item) return NULL; } +/** Sequence generic subscript (set): `object[...] = x`. */ static int Color_ass_subscript(ColorObject *self, PyObject *item, PyObject *value) { if (PyIndex_Check(item)) { @@ -388,29 +541,13 @@ static int Color_ass_subscript(ColorObject *self, PyObject *item, PyObject *valu return -1; } -/* -----------------PROTCOL DECLARATIONS-------------------------- */ -static PySequenceMethods Color_SeqMethods = { - (lenfunc)Color_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Color_item, /* sq_item */ - NULL, /* sq_slice, deprecated */ - (ssizeobjargproc)Color_ass_item, /* sq_ass_item */ - NULL, /* sq_ass_slice, deprecated */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ -}; - -static PyMappingMethods Color_AsMapping = { - (lenfunc)Color_len, - (binaryfunc)Color_subscript, - (objobjargproc)Color_ass_subscript, -}; +/** \} */ -/* numeric */ +/* -------------------------------------------------------------------- */ +/** \name Color Type: Numeric Protocol Implementation + * \{ */ -/* addition: obj + obj */ +/** Addition: `object + object`. */ static PyObject *Color_add(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -436,7 +573,7 @@ static PyObject *Color_add(PyObject *v1, PyObject *v2) return Color_CreatePyObject(col, Py_TYPE(v1)); } -/* addition in-place: obj += obj */ +/** Addition in-place: `object += object`. */ static PyObject *Color_iadd(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -463,7 +600,7 @@ static PyObject *Color_iadd(PyObject *v1, PyObject *v2) return v1; } -/* subtraction: obj - obj */ +/** Subtraction: `object - object`. */ static PyObject *Color_sub(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -489,7 +626,7 @@ static PyObject *Color_sub(PyObject *v1, PyObject *v2) return Color_CreatePyObject(col, Py_TYPE(v1)); } -/* subtraction in-place: obj -= obj */ +/** Subtraction in-place: `object -= object`. */ static PyObject *Color_isub(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -523,6 +660,7 @@ static PyObject *color_mul_float(ColorObject *color, const float scalar) return Color_CreatePyObject(tcol, Py_TYPE(color)); } +/** Multiplication: `object * object`. */ static PyObject *Color_mul(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -567,6 +705,7 @@ static PyObject *Color_mul(PyObject *v1, PyObject *v2) return NULL; } +/** Division: `object / object`. */ static PyObject *Color_div(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL; @@ -600,7 +739,7 @@ static PyObject *Color_div(PyObject *v1, PyObject *v2) return NULL; } -/* multiplication in-place: obj *= obj */ +/** Multiplication in-place: `object *= object`. */ static PyObject *Color_imul(PyObject *v1, PyObject *v2) { ColorObject *color = (ColorObject *)v1; @@ -628,7 +767,7 @@ static PyObject *Color_imul(PyObject *v1, PyObject *v2) return v1; } -/* multiplication in-place: obj *= obj */ +/** Division in-place: `object *= object`. */ static PyObject *Color_idiv(PyObject *v1, PyObject *v2) { ColorObject *color = (ColorObject *)v1; @@ -661,8 +800,7 @@ static PyObject *Color_idiv(PyObject *v1, PyObject *v2) return v1; } -/* -obj - * returns the negative of this object */ +/** Negative (returns the negative of this object): `-object`. */ static PyObject *Color_neg(ColorObject *self) { float tcol[COLOR_SIZE]; @@ -675,6 +813,31 @@ static PyObject *Color_neg(ColorObject *self) return Color_CreatePyObject(tcol, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Protocol Declarations + * \{ */ + +static PySequenceMethods Color_SeqMethods = { + (lenfunc)Color_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Color_item, /*sq_item*/ + NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Color_ass_item, /*sq_ass_item*/ + NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ +}; + +static PyMappingMethods Color_AsMapping = { + (lenfunc)Color_len, + (binaryfunc)Color_subscript, + (objobjargproc)Color_ass_subscript, +}; + static PyNumberMethods Color_NumMethods = { (binaryfunc)Color_add, /*nb_add*/ (binaryfunc)Color_sub, /*nb_subtract*/ @@ -695,24 +858,31 @@ static PyNumberMethods Color_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - Color_iadd, /* nb_inplace_add */ - Color_isub, /* nb_inplace_subtract */ - Color_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - Color_div, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - Color_idiv, /* nb_inplace_true_divide */ - NULL, /* nb_index */ + Color_iadd, /*nb_inplace_add*/ + Color_isub, /*nb_inplace_subtract*/ + Color_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + Color_div, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + Color_idiv, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ }; -/* color channel, vector.r/g/b */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Get/Set Item Implementation + * \{ */ + +/* Color channel (RGB): `color.r/g/b`. */ + PyDoc_STRVAR(Color_channel_r_doc, "Red color channel.\n\n:type: float"); PyDoc_STRVAR(Color_channel_g_doc, "Green color channel.\n\n:type: float"); PyDoc_STRVAR(Color_channel_b_doc, "Blue color channel.\n\n:type: float"); @@ -727,7 +897,8 @@ static int Color_channel_set(ColorObject *self, PyObject *value, void *type) return Color_ass_item(self, POINTER_AS_INT(type), value); } -/* color channel (HSV), color.h/s/v */ +/* Color channel (HSV): `color.h/s/v`. */ + PyDoc_STRVAR(Color_channel_hsv_h_doc, "HSV Hue component in [0, 1].\n\n:type: float"); PyDoc_STRVAR(Color_channel_hsv_s_doc, "HSV Saturation component in [0, 1].\n\n:type: float"); PyDoc_STRVAR(Color_channel_hsv_v_doc, "HSV Value component in [0, 1].\n\n:type: float"); @@ -775,8 +946,8 @@ static int Color_channel_hsv_set(ColorObject *self, PyObject *value, void *type) return 0; } -/* color channel (HSV), color.h/s/v */ PyDoc_STRVAR(Color_hsv_doc, "HSV Values in [0, 1].\n\n:type: float triplet"); +/** Color channel HSV (get): `x = color.hsv`. */ static PyObject *Color_hsv_get(ColorObject *self, void *UNUSED(closure)) { float hsv[3]; @@ -794,6 +965,7 @@ static PyObject *Color_hsv_get(ColorObject *self, void *UNUSED(closure)) return ret; } +/** Color channel HSV (set): `color.hsv = x`. */ static int Color_hsv_set(ColorObject *self, PyObject *value, void *UNUSED(closure)) { float hsv[3]; @@ -816,9 +988,12 @@ static int Color_hsv_set(ColorObject *self, PyObject *value, void *UNUSED(closur return 0; } -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Color_getseters[] = { {"r", (getter)Color_channel_get, (setter)Color_channel_set, Color_channel_r_doc, (void *)0}, {"g", (getter)Color_channel_get, (setter)Color_channel_set, Color_channel_g_doc, (void *)1}, @@ -861,7 +1036,12 @@ static PyGetSetDef Color_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/* -----------------------METHOD DEFINITIONS ---------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Method Definitions + * \{ */ + static struct PyMethodDef Color_methods[] = { {"copy", (PyCFunction)Color_copy, METH_NOARGS, Color_copy_doc}, {"__copy__", (PyCFunction)Color_copy, METH_NOARGS, Color_copy_doc}, @@ -869,17 +1049,61 @@ static struct PyMethodDef Color_methods[] = { /* base-math methods */ {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, + + /* Color-space methods. */ + {"from_scene_linear_to_srgb", + (PyCFunction)Color_from_scene_linear_to_srgb, + METH_NOARGS, + Color_from_scene_linear_to_srgb_doc}, + {"from_srgb_to_scene_linear", + (PyCFunction)Color_from_srgb_to_scene_linear, + METH_NOARGS, + Color_from_srgb_to_scene_linear_doc}, + {"from_scene_linear_to_xyz_d65", + (PyCFunction)Color_from_scene_linear_to_xyz_d65, + METH_NOARGS, + Color_from_scene_linear_to_xyz_d65_doc}, + {"from_xyz_d65_to_scene_linear", + (PyCFunction)Color_from_xyz_d65_to_scene_linear, + METH_NOARGS, + Color_from_xyz_d65_to_scene_linear_doc}, + {"from_scene_linear_to_aces", + (PyCFunction)Color_from_scene_linear_to_aces, + METH_NOARGS, + Color_from_scene_linear_to_aces_doc}, + {"from_aces_to_scene_linear", + (PyCFunction)Color_from_aces_to_scene_linear, + METH_NOARGS, + Color_from_aces_to_scene_linear_doc}, + {"from_scene_linear_to_rec709_linear", + (PyCFunction)Color_from_scene_linear_to_rec709_linear, + METH_NOARGS, + Color_from_scene_linear_to_rec709_linear_doc}, + {"from_rec709_linear_to_scene_linear", + (PyCFunction)Color_from_rec709_linear_to_scene_linear, + METH_NOARGS, + Color_from_rec709_linear_to_scene_linear_doc}, {NULL, NULL, 0, NULL}, }; -/* ------------------PY_OBECT DEFINITION-------------------------- */ -PyDoc_STRVAR(color_doc, - ".. class:: Color(rgb)\n" - "\n" - " This object gives access to Colors in Blender.\n" - "\n" - " :param rgb: (r, g, b) color values\n" - " :type rgb: 3d vector\n"); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Python Object Definition + * \{ */ + +PyDoc_STRVAR( + color_doc, + ".. class:: Color(rgb)\n" + "\n" + " This object gives access to Colors in Blender.\n" + "\n" + " Most colors returned by Blender APIs are in scene linear color space, as defined by " + " the OpenColorIO configuration. The notable exception is user interface theming colors, " + " which are in sRGB color space.\n" + "\n" + " :param rgb: (r, g, b) color values\n" + " :type rgb: 3d vector\n"); PyTypeObject color_Type = { PyVarObject_HEAD_INIT(NULL, 0) "Color", /* tp_name */ sizeof(ColorObject), /* tp_basicsize */ @@ -932,6 +1156,12 @@ PyTypeObject color_Type = { NULL, /* tp_del */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: C/API Constructors + * \{ */ + PyObject *Color_CreatePyObject(const float col[3], PyTypeObject *base_type) { ColorObject *self; @@ -1001,3 +1231,5 @@ PyObject *Color_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar cb_sub return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Color.h b/source/blender/python/mathutils/mathutils_Color.h index 11a936b7bc8..b4fd2eeaa81 100644 --- a/source/blender/python/mathutils/mathutils_Color.h +++ b/source/blender/python/mathutils/mathutils_Color.h @@ -19,7 +19,8 @@ typedef struct { * be stored in py_data) or be a wrapper for data allocated through * Blender (stored in blend_data). This is an either/or struct not both. */ -/* prototypes */ +/* Prototypes. */ + PyObject *Color_CreatePyObject(const float col[3], PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; PyObject *Color_CreatePyObject_wrap(float col[3], PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index 909c19e8cd2..f49868dfba7 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -19,45 +19,11 @@ #define EULER_SIZE 3 -/* ----------------------------------mathutils.Euler() ------------------- */ -/* makes a new euler for you to play with */ -static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *seq = NULL; - const char *order_str = NULL; - - float eul[EULER_SIZE] = {0.0f, 0.0f, 0.0f}; - short order = EULER_ORDER_XYZ; - - if (kwds && PyDict_Size(kwds)) { - PyErr_SetString(PyExc_TypeError, - "mathutils.Euler(): " - "takes no keyword args"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "|Os:mathutils.Euler", &seq, &order_str)) { - return NULL; - } - - switch (PyTuple_GET_SIZE(args)) { - case 0: - break; - case 2: - if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1) { - return NULL; - } - ATTR_FALLTHROUGH; - case 1: - if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1) { - return NULL; - } - break; - } - return Euler_CreatePyObject(eul, order, type); -} +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ -/* internal use, assume read callback is done */ +/** Internal use, assume read callback is done. */ static const char *euler_order_str(EulerObject *self) { static const char order[][4] = {"XYZ", "XZY", "YXZ", "YZX", "ZXY", "ZYX"}; @@ -96,8 +62,10 @@ short euler_order_from_string(const char *str, const char *error_prefix) return -1; } -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ -static PyObject *Euler_ToTupleExt(EulerObject *self, int ndigits) +/** + * \note #BaseMath_ReadCallback must be called beforehand. + */ +static PyObject *Euler_to_tuple_ex(EulerObject *self, int ndigits) { PyObject *ret; int i; @@ -118,8 +86,53 @@ static PyObject *Euler_ToTupleExt(EulerObject *self, int ndigits) return ret; } -/* -----------------------------METHODS---------------------------- - * return a quaternion representation of the euler */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: `__new__` / `mathutils.Euler()` + * \{ */ + +static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *seq = NULL; + const char *order_str = NULL; + + float eul[EULER_SIZE] = {0.0f, 0.0f, 0.0f}; + short order = EULER_ORDER_XYZ; + + if (kwds && PyDict_Size(kwds)) { + PyErr_SetString(PyExc_TypeError, + "mathutils.Euler(): " + "takes no keyword args"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "|Os:mathutils.Euler", &seq, &order_str)) { + return NULL; + } + + switch (PyTuple_GET_SIZE(args)) { + case 0: + break; + case 2: + if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1) { + return NULL; + } + ATTR_FALLTHROUGH; + case 1: + if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1) { + return NULL; + } + break; + } + return Euler_CreatePyObject(eul, order, type); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Methods + * \{ */ PyDoc_STRVAR(Euler_to_quaternion_doc, ".. method:: to_quaternion()\n" @@ -141,7 +154,6 @@ static PyObject *Euler_to_quaternion(EulerObject *self) return Quaternion_CreatePyObject(quat, NULL); } -/* return a matrix representation of the euler */ PyDoc_STRVAR(Euler_to_matrix_doc, ".. method:: to_matrix()\n" "\n" @@ -279,9 +291,6 @@ static PyObject *Euler_make_compatible(EulerObject *self, PyObject *value) Py_RETURN_NONE; } -/* ----------------------------Euler.rotate()----------------------- - * return a copy of the euler */ - PyDoc_STRVAR(Euler_copy_doc, ".. function:: copy()\n" "\n" @@ -308,8 +317,11 @@ static PyObject *Euler_deepcopy(EulerObject *self, PyObject *args) return Euler_copy(self); } -/* ----------------------------print object (internal)-------------- - * print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: `__repr__` & `__str__` + * \{ */ static PyObject *Euler_repr(EulerObject *self) { @@ -319,7 +331,7 @@ static PyObject *Euler_repr(EulerObject *self) return NULL; } - tuple = Euler_ToTupleExt(self, -1); + tuple = Euler_to_tuple_ex(self, -1); ret = PyUnicode_FromFormat("Euler(%R, '%s')", tuple, euler_order_str(self)); @@ -349,6 +361,12 @@ static PyObject *Euler_str(EulerObject *self) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Rich Compare + * \{ */ + static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -390,6 +408,12 @@ static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Euler_hash(EulerObject *self) { if (BaseMath_ReadCallback(self) == -1) { @@ -403,15 +427,19 @@ static Py_hash_t Euler_hash(EulerObject *self) return mathutils_array_hash(self->eul, EULER_SIZE); } -/* ---------------------SEQUENCE PROTOCOLS------------------------ */ -/* ----------------------------len(object)------------------------ */ -/* sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Sequence Protocol + * \{ */ + +/** Sequence length: `len(object)`. */ static int Euler_len(EulerObject *UNUSED(self)) { return EULER_SIZE; } -/* ----------------------------object[]--------------------------- */ -/* sequence accessor (get) */ + +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Euler_item(EulerObject *self, int i) { if (i < 0) { @@ -431,8 +459,8 @@ static PyObject *Euler_item(EulerObject *self, int i) return PyFloat_FromDouble(self->eul[i]); } -/* ----------------------------object[]------------------------- */ -/* sequence accessor (set) */ + +/** Sequence accessor (set): `object[i] = x`. */ static int Euler_ass_item(EulerObject *self, int i, PyObject *value) { float f; @@ -468,8 +496,8 @@ static int Euler_ass_item(EulerObject *self, int i, PyObject *value) return 0; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (get) */ + +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Euler_slice(EulerObject *self, int begin, int end) { PyObject *tuple; @@ -493,8 +521,8 @@ static PyObject *Euler_slice(EulerObject *self, int begin, int end) return tuple; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (set) */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Euler_ass_slice(EulerObject *self, int begin, int end, PyObject *seq) { int i, size; @@ -531,6 +559,7 @@ static int Euler_ass_slice(EulerObject *self, int begin, int end, PyObject *seq) return 0; } +/** Sequence generic subscript (get): `x = object[...]`. */ static PyObject *Euler_subscript(EulerObject *self, PyObject *item) { if (PyIndex_Check(item)) { @@ -567,6 +596,7 @@ static PyObject *Euler_subscript(EulerObject *self, PyObject *item) return NULL; } +/** Sequence generic subscript (set): `object[...] = x`. */ static int Euler_ass_subscript(EulerObject *self, PyObject *item, PyObject *value) { if (PyIndex_Check(item)) { @@ -599,18 +629,23 @@ static int Euler_ass_subscript(EulerObject *self, PyObject *item, PyObject *valu return -1; } -/* -----------------PROTCOL DECLARATIONS-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Sequence & Mapping Protocol Declarations + * \{ */ + static PySequenceMethods Euler_SeqMethods = { - (lenfunc)Euler_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Euler_item, /* sq_item */ - (ssizessizeargfunc)NULL, /* sq_slice (deprecated) */ - (ssizeobjargproc)Euler_ass_item, /* sq_ass_item */ - (ssizessizeobjargproc)NULL, /* sq_ass_slice (deprecated) */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ + (lenfunc)Euler_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Euler_item, /*sq_item*/ + (ssizessizeargfunc)NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Euler_ass_item, /*sq_ass_item*/ + (ssizessizeobjargproc)NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ }; static PyMappingMethods Euler_AsMapping = { @@ -619,7 +654,13 @@ static PyMappingMethods Euler_AsMapping = { (objobjargproc)Euler_ass_subscript, }; -/* euler axis, euler.x/y/z */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Get/Set Item Implementation + * \{ */ + +/* Euler axis: `euler.x/y/z`. */ PyDoc_STRVAR(Euler_axis_doc, "Euler axis angle in radians.\n\n:type: float"); static PyObject *Euler_axis_get(EulerObject *self, void *type) @@ -632,7 +673,7 @@ static int Euler_axis_set(EulerObject *self, PyObject *value, void *type) return Euler_ass_item(self, POINTER_AS_INT(type), value); } -/* rotation order */ +/* Euler rotation order: `euler.order`. */ PyDoc_STRVAR( Euler_order_doc, @@ -666,9 +707,12 @@ static int Euler_order_set(EulerObject *self, PyObject *value, void *UNUSED(clos return 0; } -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Euler_getseters[] = { {"x", (getter)Euler_axis_get, (setter)Euler_axis_set, Euler_axis_doc, (void *)0}, {"y", (getter)Euler_axis_get, (setter)Euler_axis_set, Euler_axis_doc, (void *)1}, @@ -694,7 +738,12 @@ static PyGetSetDef Euler_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/* -----------------------METHOD DEFINITIONS ---------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Method Definitions + * \{ */ + static struct PyMethodDef Euler_methods[] = { {"zero", (PyCFunction)Euler_zero, METH_NOARGS, Euler_zero_doc}, {"to_matrix", (PyCFunction)Euler_to_matrix, METH_NOARGS, Euler_to_matrix_doc}, @@ -711,7 +760,12 @@ static struct PyMethodDef Euler_methods[] = { {NULL, NULL, 0, NULL}, }; -/* ------------------PY_OBECT DEFINITION-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Python Object Definition + * \{ */ + PyDoc_STRVAR( euler_doc, ".. class:: Euler(angles, order='XYZ')\n" @@ -776,6 +830,12 @@ PyTypeObject euler_Type = { NULL, /* tp_del */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: C/API Constructors + * \{ */ + PyObject *Euler_CreatePyObject(const float eul[3], const short order, PyTypeObject *base_type) { EulerObject *self; @@ -849,3 +909,5 @@ PyObject *Euler_CreatePyObject_cb(PyObject *cb_user, return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Euler.h b/source/blender/python/mathutils/mathutils_Euler.h index 4438ee380af..ca2ca26c90a 100644 --- a/source/blender/python/mathutils/mathutils_Euler.h +++ b/source/blender/python/mathutils/mathutils_Euler.h @@ -22,6 +22,7 @@ typedef struct { * blender (stored in blend_data). This is an either/or struct not both */ /* prototypes */ + PyObject *Euler_CreatePyObject(const float eul[3], short order, PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 76b5424711f..8cd7a5c7d87 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -32,6 +32,10 @@ static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), MatrixObject *self); static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrixAccess_t type); +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + static int matrix_row_vector_check(MatrixObject *mat, VectorObject *vec, int row) { if ((vec->vec_num != mat->col_num) || (row >= mat->row_num)) { @@ -56,9 +60,242 @@ static int matrix_col_vector_check(MatrixObject *mat, VectorObject *vec, int col return 1; } -/* ---------------------------------------------------------------------------- - * matrix row callbacks - * this is so you can do matrix[i][j] = val OR matrix.row[i][j] = val */ +/** When a matrix is 4x4 size but initialized as a 3x3, re-assign values for 4x4. */ +static void matrix_3x3_as_4x4(float mat[16]) +{ + mat[10] = mat[8]; + mat[9] = mat[7]; + mat[8] = mat[6]; + mat[7] = 0.0f; + mat[6] = mat[5]; + mat[5] = mat[4]; + mat[4] = mat[3]; + mat[3] = 0.0f; +} + +void matrix_as_3x3(float mat[3][3], MatrixObject *self) +{ + copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0)); + copy_v3_v3(mat[1], MATRIX_COL_PTR(self, 1)); + copy_v3_v3(mat[2], MATRIX_COL_PTR(self, 2)); +} + +static void matrix_copy(MatrixObject *mat_dst, const MatrixObject *mat_src) +{ + BLI_assert((mat_dst->col_num == mat_src->col_num) && (mat_dst->row_num == mat_src->row_num)); + BLI_assert(mat_dst != mat_src); + + memcpy(mat_dst->matrix, mat_src->matrix, sizeof(float) * (mat_dst->col_num * mat_dst->row_num)); +} + +static void matrix_unit_internal(MatrixObject *self) +{ + const int mat_size = sizeof(float) * (self->col_num * self->row_num); + memset(self->matrix, 0x0, mat_size); + const int col_row_max = min_ii(self->col_num, self->row_num); + const int row_num = self->row_num; + for (int col = 0; col < col_row_max; col++) { + self->matrix[(col * row_num) + col] = 1.0f; + } +} + +/** Transposes memory layout, row/columns don't have to match. */ +static void matrix_transpose_internal(float mat_dst_fl[], const MatrixObject *mat_src) +{ + ushort col, row; + uint i = 0; + + for (row = 0; row < mat_src->row_num; row++) { + for (col = 0; col < mat_src->col_num; col++) { + mat_dst_fl[i++] = MATRIX_ITEM(mat_src, row, col); + } + } +} + +/** Assumes `rowsize == colsize` is checked and the read callback has run. */ +static float matrix_determinant_internal(const MatrixObject *self) +{ + if (self->col_num == 2) { + return determinant_m2(MATRIX_ITEM(self, 0, 0), + MATRIX_ITEM(self, 0, 1), + MATRIX_ITEM(self, 1, 0), + MATRIX_ITEM(self, 1, 1)); + } + if (self->col_num == 3) { + return determinant_m3(MATRIX_ITEM(self, 0, 0), + MATRIX_ITEM(self, 0, 1), + MATRIX_ITEM(self, 0, 2), + MATRIX_ITEM(self, 1, 0), + MATRIX_ITEM(self, 1, 1), + MATRIX_ITEM(self, 1, 2), + MATRIX_ITEM(self, 2, 0), + MATRIX_ITEM(self, 2, 1), + MATRIX_ITEM(self, 2, 2)); + } + + return determinant_m4((const float(*)[4])self->matrix); +} + +static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim) +{ + /* calculate the classical adjoint */ + switch (dim) { + case 2: { + adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src); + break; + } + case 3: { + adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src); + break; + } + case 4: { + adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src); + break; + } + default: + BLI_assert_unreachable(); + break; + } +} + +static void matrix_invert_with_det_n_internal(float *mat_dst, + const float *mat_src, + const float det, + const ushort dim) +{ + float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; + ushort i, j, k; + + BLI_assert(det != 0.0f); + + adjoint_matrix_n(mat, mat_src, dim); + + /* divide by determinant & set values */ + k = 0; + for (i = 0; i < dim; i++) { /* col_num */ + for (j = 0; j < dim; j++) { /* row_num */ + mat_dst[MATRIX_ITEM_INDEX_NUMROW(dim, j, i)] = mat[k++] / det; + } + } +} + +/** + * \param r_mat: can be from `self->matrix` or not. + */ +static bool matrix_invert_internal(const MatrixObject *self, float *r_mat) +{ + float det; + BLI_assert(self->col_num == self->row_num); + det = matrix_determinant_internal(self); + + if (det != 0.0f) { + matrix_invert_with_det_n_internal(r_mat, self->matrix, det, self->col_num); + return true; + } + + return false; +} + +/** + * Similar to `matrix_invert_internal` but should never error. + * \param r_mat: can be from `self->matrix` or not. + */ +static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) +{ + float det; + float *in_mat = self->matrix; + BLI_assert(self->col_num == self->row_num); + det = matrix_determinant_internal(self); + + if (det == 0.0f) { + const float eps = PSEUDOINVERSE_EPSILON; + + /* We will copy self->matrix into r_mat (if needed), + * and modify it in place to add diagonal epsilon. */ + in_mat = r_mat; + + switch (self->col_num) { + case 2: { + float(*mat)[2] = (float(*)[2])in_mat; + + if (in_mat != self->matrix) { + copy_m2_m2(mat, (const float(*)[2])self->matrix); + } + mat[0][0] += eps; + mat[1][1] += eps; + + if (UNLIKELY((det = determinant_m2(mat[0][0], mat[0][1], mat[1][0], mat[1][1])) == 0.0f)) { + unit_m2(mat); + det = 1.0f; + } + break; + } + case 3: { + float(*mat)[3] = (float(*)[3])in_mat; + + if (in_mat != self->matrix) { + copy_m3_m3(mat, (const float(*)[3])self->matrix); + } + mat[0][0] += eps; + mat[1][1] += eps; + mat[2][2] += eps; + + if (UNLIKELY((det = determinant_m3_array(mat)) == 0.0f)) { + unit_m3(mat); + det = 1.0f; + } + break; + } + case 4: { + float(*mat)[4] = (float(*)[4])in_mat; + + if (in_mat != self->matrix) { + copy_m4_m4(mat, (const float(*)[4])self->matrix); + } + mat[0][0] += eps; + mat[1][1] += eps; + mat[2][2] += eps; + mat[3][3] += eps; + + if (UNLIKELY(det = determinant_m4(mat)) == 0.0f) { + unit_m4(mat); + det = 1.0f; + } + break; + } + default: + BLI_assert_unreachable(); + } + } + + matrix_invert_with_det_n_internal(r_mat, in_mat, det, self->col_num); +} + +static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), + MatrixObject *self) +{ + PyObject *ret = Matrix_copy(self); + if (ret) { + PyObject *ret_dummy = matrix_func((MatrixObject *)ret); + if (ret_dummy) { + Py_DECREF(ret_dummy); + return ret; + } + /* error */ + Py_DECREF(ret); + return NULL; + } + + /* copy may fail if the read callback errors out */ + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Row Callbacks + * This is so you can do `matrix[i][j] = val` or `matrix.row[i][j] = val`. + * \{ */ uchar mathutils_matrix_row_cb_index = -1; @@ -147,9 +384,12 @@ Mathutils_Callback mathutils_matrix_row_cb = { mathutils_matrix_row_set_index, }; -/* ---------------------------------------------------------------------------- - * matrix row callbacks - * this is so you can do matrix.col[i][j] = val */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Column Callbacks + * This is so you can do `matrix.col[i][j] = val`. + * \{ */ uchar mathutils_matrix_col_cb_index = -1; @@ -246,10 +486,14 @@ Mathutils_Callback mathutils_matrix_col_cb = { mathutils_matrix_col_set_index, }; -/* ---------------------------------------------------------------------------- - * matrix row callbacks - * this is so you can do matrix.translation = val - * NOTE: this is _exactly like matrix.col except the 4th component is always omitted. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Translation Callbacks + * This is so you can do `matrix.translation = val`. + * + * \note this is _exactly like matrix.col except the 4th component is always omitted. + * \{ */ uchar mathutils_matrix_translation_cb_index = -1; @@ -326,11 +570,12 @@ Mathutils_Callback mathutils_matrix_translation_cb = { mathutils_matrix_translation_set_index, }; -/* matrix column callbacks, this is so you can do `matrix.translation = Vector()`. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: `__new__` / `mathutils.Matrix()` + * \{ */ -/* ----------------------------------mathutils.Matrix() ----------------- */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ -/* create a new matrix type */ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { if (kwds && PyDict_Size(kwds)) { @@ -379,41 +624,13 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } -static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), - MatrixObject *self) -{ - PyObject *ret = Matrix_copy(self); - if (ret) { - PyObject *ret_dummy = matrix_func((MatrixObject *)ret); - if (ret_dummy) { - Py_DECREF(ret_dummy); - return ret; - } - /* error */ - Py_DECREF(ret); - return NULL; - } - - /* copy may fail if the read callback errors out */ - return NULL; -} - -/* when a matrix is 4x4 size but initialized as a 3x3, re-assign values for 4x4 */ -static void matrix_3x3_as_4x4(float mat[16]) -{ - mat[10] = mat[8]; - mat[9] = mat[7]; - mat[8] = mat[6]; - mat[7] = 0.0f; - mat[6] = mat[5]; - mat[5] = mat[4]; - mat[4] = mat[3]; - mat[3] = 0.0f; -} +/** \} */ -/*-----------------------CLASS-METHODS----------------------------*/ +/* -------------------------------------------------------------------- */ +/** \name Matrix Class Methods + * \{ */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ +/** Identity constructor: `mathutils.Matrix.Identity()`. */ PyDoc_STRVAR(C_Matrix_Identity_doc, ".. classmethod:: Identity(size)\n" "\n" @@ -441,6 +658,7 @@ static PyObject *C_Matrix_Identity(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(NULL, matSize, matSize, (PyTypeObject *)cls); } +/** Rotation constructor: `mathutils.Matrix.Rotation()`. */ PyDoc_STRVAR(C_Matrix_Rotation_doc, ".. classmethod:: Rotation(angle, size, axis)\n" "\n" @@ -460,25 +678,8 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) PyObject *vec = NULL; const char *axis = NULL; int matSize; - double angle; /* use double because of precision problems at high values */ - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + double angle; /* Use double because of precision problems at high values. */ + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "di|O:Matrix.Rotation", &angle, &matSize, &vec)) { return NULL; @@ -545,6 +746,7 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } +/** Translation constructor: `mathutils.Matrix.Translation()`. */ PyDoc_STRVAR(C_Matrix_Translation_doc, ".. classmethod:: Translation(vector)\n" "\n" @@ -567,7 +769,7 @@ static PyObject *C_Matrix_Translation(PyObject *cls, PyObject *value) return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls); } -/* ----------------------------------mathutils.Matrix.Diagonal() ------------- */ + PyDoc_STRVAR(C_Matrix_Diagonal_doc, ".. classmethod:: Diagonal(vector)\n" "\n" @@ -577,6 +779,7 @@ PyDoc_STRVAR(C_Matrix_Diagonal_doc, " :type vector: :class:`Vector`\n" " :return: A diagonal matrix.\n" " :rtype: :class:`Matrix`\n"); +/** Diagonal constructor: `mathutils.Matrix.Diagonal()`. */ static PyObject *C_Matrix_Diagonal(PyObject *cls, PyObject *value) { float mat[16] = {0.0f}; @@ -595,8 +798,8 @@ static PyObject *C_Matrix_Diagonal(PyObject *cls, PyObject *value) return Matrix_CreatePyObject(mat, size, size, (PyTypeObject *)cls); } -/* ----------------------------------mathutils.Matrix.Scale() ------------- */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ + +/** Scale constructor: `mathutils.Matrix.Scale()`. */ PyDoc_STRVAR(C_Matrix_Scale_doc, ".. classmethod:: Scale(factor, size, axis)\n" "\n" @@ -617,24 +820,7 @@ static PyObject *C_Matrix_Scale(PyObject *cls, PyObject *args) float tvec[3]; float factor; int matSize; - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "fi|O:Matrix.Scale", &factor, &matSize, &vec)) { return NULL; @@ -700,8 +886,7 @@ static PyObject *C_Matrix_Scale(PyObject *cls, PyObject *args) /* pass to matrix creation */ return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } -/* ----------------------------------mathutils.Matrix.OrthoProjection() --- */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ +/** Orthographic projection constructor: `mathutils.Matrix.OrthoProjection()`. */ PyDoc_STRVAR(C_Matrix_OrthoProjection_doc, ".. classmethod:: OrthoProjection(axis, size)\n" "\n" @@ -721,24 +906,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) int matSize, x; float norm = 0.0f; - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "Oi:Matrix.OrthoProjection", &axis, &matSize)) { return NULL; @@ -837,6 +1005,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } +/** Shear constructor: `mathutils.Matrix.Shear()`. */ PyDoc_STRVAR(C_Matrix_Shear_doc, ".. classmethod:: Shear(plane, size, factor)\n" "\n" @@ -857,24 +1026,7 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args) int matSize; const char *plane; PyObject *fac; - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "siO:Matrix.Shear", &plane, &matSize, &fac)) { return NULL; @@ -1051,205 +1203,12 @@ static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls); } -void matrix_as_3x3(float mat[3][3], MatrixObject *self) -{ - copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0)); - copy_v3_v3(mat[1], MATRIX_COL_PTR(self, 1)); - copy_v3_v3(mat[2], MATRIX_COL_PTR(self, 2)); -} - -static void matrix_copy(MatrixObject *mat_dst, const MatrixObject *mat_src) -{ - BLI_assert((mat_dst->col_num == mat_src->col_num) && (mat_dst->row_num == mat_src->row_num)); - BLI_assert(mat_dst != mat_src); +/** \} */ - memcpy(mat_dst->matrix, mat_src->matrix, sizeof(float) * (mat_dst->col_num * mat_dst->row_num)); -} +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To Quaternion + * \{ */ -static void matrix_unit_internal(MatrixObject *self) -{ - const int mat_size = sizeof(float) * (self->col_num * self->row_num); - memset(self->matrix, 0x0, mat_size); - const int col_row_max = min_ii(self->col_num, self->row_num); - const int row_num = self->row_num; - for (int col = 0; col < col_row_max; col++) { - self->matrix[(col * row_num) + col] = 1.0f; - } -} - -/* transposes memory layout, rol/col's don't have to match */ -static void matrix_transpose_internal(float mat_dst_fl[], const MatrixObject *mat_src) -{ - ushort col, row; - uint i = 0; - - for (row = 0; row < mat_src->row_num; row++) { - for (col = 0; col < mat_src->col_num; col++) { - mat_dst_fl[i++] = MATRIX_ITEM(mat_src, row, col); - } - } -} - -/* assumes rowsize == colsize is checked and the read callback has run */ -static float matrix_determinant_internal(const MatrixObject *self) -{ - if (self->col_num == 2) { - return determinant_m2(MATRIX_ITEM(self, 0, 0), - MATRIX_ITEM(self, 0, 1), - MATRIX_ITEM(self, 1, 0), - MATRIX_ITEM(self, 1, 1)); - } - if (self->col_num == 3) { - return determinant_m3(MATRIX_ITEM(self, 0, 0), - MATRIX_ITEM(self, 0, 1), - MATRIX_ITEM(self, 0, 2), - MATRIX_ITEM(self, 1, 0), - MATRIX_ITEM(self, 1, 1), - MATRIX_ITEM(self, 1, 2), - MATRIX_ITEM(self, 2, 0), - MATRIX_ITEM(self, 2, 1), - MATRIX_ITEM(self, 2, 2)); - } - - return determinant_m4((const float(*)[4])self->matrix); -} - -static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim) -{ - /* calculate the classical adjoint */ - switch (dim) { - case 2: { - adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src); - break; - } - case 3: { - adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src); - break; - } - case 4: { - adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src); - break; - } - default: - BLI_assert_unreachable(); - break; - } -} - -static void matrix_invert_with_det_n_internal(float *mat_dst, - const float *mat_src, - const float det, - const ushort dim) -{ - float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; - ushort i, j, k; - - BLI_assert(det != 0.0f); - - adjoint_matrix_n(mat, mat_src, dim); - - /* divide by determinant & set values */ - k = 0; - for (i = 0; i < dim; i++) { /* col_num */ - for (j = 0; j < dim; j++) { /* row_num */ - mat_dst[MATRIX_ITEM_INDEX_NUMROW(dim, j, i)] = mat[k++] / det; - } - } -} - -/** - * \param r_mat: can be from `self->matrix` or not. - */ -static bool matrix_invert_internal(const MatrixObject *self, float *r_mat) -{ - float det; - BLI_assert(self->col_num == self->row_num); - det = matrix_determinant_internal(self); - - if (det != 0.0f) { - matrix_invert_with_det_n_internal(r_mat, self->matrix, det, self->col_num); - return true; - } - - return false; -} - -/** - * Similar to `matrix_invert_internal` but should never error. - * \param r_mat: can be from `self->matrix` or not. - */ -static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) -{ - float det; - float *in_mat = self->matrix; - BLI_assert(self->col_num == self->row_num); - det = matrix_determinant_internal(self); - - if (det == 0.0f) { - const float eps = PSEUDOINVERSE_EPSILON; - - /* We will copy self->matrix into r_mat (if needed), - * and modify it in place to add diagonal epsilon. */ - in_mat = r_mat; - - switch (self->col_num) { - case 2: { - float(*mat)[2] = (float(*)[2])in_mat; - - if (in_mat != self->matrix) { - copy_m2_m2(mat, (const float(*)[2])self->matrix); - } - mat[0][0] += eps; - mat[1][1] += eps; - - if (UNLIKELY((det = determinant_m2(mat[0][0], mat[0][1], mat[1][0], mat[1][1])) == 0.0f)) { - unit_m2(mat); - det = 1.0f; - } - break; - } - case 3: { - float(*mat)[3] = (float(*)[3])in_mat; - - if (in_mat != self->matrix) { - copy_m3_m3(mat, (const float(*)[3])self->matrix); - } - mat[0][0] += eps; - mat[1][1] += eps; - mat[2][2] += eps; - - if (UNLIKELY((det = determinant_m3_array(mat)) == 0.0f)) { - unit_m3(mat); - det = 1.0f; - } - break; - } - case 4: { - float(*mat)[4] = (float(*)[4])in_mat; - - if (in_mat != self->matrix) { - copy_m4_m4(mat, (const float(*)[4])self->matrix); - } - mat[0][0] += eps; - mat[1][1] += eps; - mat[2][2] += eps; - mat[3][3] += eps; - - if (UNLIKELY(det = determinant_m4(mat)) == 0.0f) { - unit_m4(mat); - det = 1.0f; - } - break; - } - default: - BLI_assert_unreachable(); - } - } - - matrix_invert_with_det_n_internal(r_mat, in_mat, det, self->col_num); -} - -/*-----------------------------METHODS----------------------------*/ PyDoc_STRVAR(Matrix_to_quaternion_doc, ".. method:: to_quaternion()\n" "\n" @@ -1282,7 +1241,12 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self) return Quaternion_CreatePyObject(quat, NULL); } -/*---------------------------matrix.toEuler() --------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To Euler + * \{ */ + PyDoc_STRVAR(Matrix_to_euler_doc, ".. method:: to_euler(order, euler_compat)\n" "\n" @@ -1367,6 +1331,12 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args) return Euler_CreatePyObject(eul, order, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Resize + * \{ */ + PyDoc_STRVAR(Matrix_resize_4x4_doc, ".. method:: resize_4x4()\n" "\n" @@ -1411,6 +1381,12 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To NxN + * \{ */ + static PyObject *Matrix_to_NxN(MatrixObject *self, const int col_num, const int row_num) { const int mat_size = sizeof(float) * (col_num * row_num); @@ -1480,6 +1456,12 @@ static PyObject *Matrix_to_4x4(MatrixObject *self) return Matrix_to_NxN(self, 4, 4); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To Translation/Scale + * \{ */ + PyDoc_STRVAR(Matrix_to_translation_doc, ".. method:: to_translation()\n" "\n" @@ -1539,9 +1521,13 @@ static PyObject *Matrix_to_scale(MatrixObject *self) return Vector_CreatePyObject(size, 3, NULL); } -/*---------------------------matrix.invert() ---------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Invert + * \{ */ -/* re-usable checks for invert */ +/** Re-usable checks for invert. */ static bool matrix_invert_is_compat(const MatrixObject *self) { if (self->col_num != self->row_num) { @@ -1763,7 +1749,12 @@ static PyObject *Matrix_inverted_safe(MatrixObject *self) return Matrix_copy_notest(self, mat); } -/*---------------------------matrix.adjugate() ---------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Adjugate + * \{ */ + PyDoc_STRVAR( Matrix_adjugate_doc, ".. method:: adjugate()\n" @@ -1852,7 +1843,12 @@ static PyObject *Matrix_rotate(MatrixObject *self, PyObject *value) Py_RETURN_NONE; } -/*---------------------------matrix.decompose() ---------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Decompose + * \{ */ + PyDoc_STRVAR(Matrix_decompose_doc, ".. method:: decompose()\n" "\n" @@ -1890,6 +1886,12 @@ static PyObject *Matrix_decompose(MatrixObject *self) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Linear Interpolate (lerp) + * \{ */ + PyDoc_STRVAR(Matrix_lerp_doc, ".. function:: lerp(other, factor)\n" "\n" @@ -1947,7 +1949,6 @@ static PyObject *Matrix_lerp(MatrixObject *self, PyObject *args) return Matrix_CreatePyObject(mat, self->col_num, self->row_num, Py_TYPE(self)); } -/*---------------------------matrix.determinant() ----------------*/ PyDoc_STRVAR( Matrix_determinant_doc, ".. method:: determinant()\n" @@ -1973,7 +1974,13 @@ static PyObject *Matrix_determinant(MatrixObject *self) return PyFloat_FromDouble((double)matrix_determinant_internal(self)); } -/*---------------------------matrix.transpose() ------------------*/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Transpose + * \{ */ + PyDoc_STRVAR( Matrix_transpose_doc, ".. method:: transpose()\n" @@ -2022,7 +2029,12 @@ static PyObject *Matrix_transposed(MatrixObject *self) return matrix__apply_to_copy(Matrix_transpose, self); } -/*---------------------------matrix.normalize() ------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Normalize + * \{ */ + PyDoc_STRVAR(Matrix_normalize_doc, ".. method:: normalize()\n" "\n" @@ -2068,7 +2080,12 @@ static PyObject *Matrix_normalized(MatrixObject *self) return matrix__apply_to_copy(Matrix_normalize, self); } -/*---------------------------matrix.zero() -----------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Zero + * \{ */ + PyDoc_STRVAR(Matrix_zero_doc, ".. method:: zero()\n" "\n" @@ -2089,7 +2106,13 @@ static PyObject *Matrix_zero(MatrixObject *self) Py_RETURN_NONE; } -/*---------------------------matrix.identity(() ------------------*/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Set Identity + * \{ */ + static void matrix_identity_internal(MatrixObject *self) { BLI_assert((self->col_num == self->row_num) && (self->row_num <= 4)); @@ -2137,8 +2160,13 @@ static PyObject *Matrix_identity(MatrixObject *self) Py_RETURN_NONE; } -/*---------------------------Matrix.copy() ------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Copy + * \{ */ +/** Copy `Matrix.copy()` */ static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix) { return Matrix_CreatePyObject((const float *)matrix, self->col_num, self->row_num, Py_TYPE(self)); @@ -2159,6 +2187,8 @@ static PyObject *Matrix_copy(MatrixObject *self) return Matrix_copy_notest(self, self->matrix); } + +/** Deep-copy `Matrix.deepcopy()` */ static PyObject *Matrix_deepcopy(MatrixObject *self, PyObject *args) { if (!PyC_CheckArgs_DeepCopy(args)) { @@ -2167,8 +2197,12 @@ static PyObject *Matrix_deepcopy(MatrixObject *self, PyObject *args) return Matrix_copy(self); } -/*----------------------------print object (internal)-------------*/ -/* print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: `__repr__` & `__str__` + * \{ */ + static PyObject *Matrix_repr(MatrixObject *self) { int col, row; @@ -2257,6 +2291,12 @@ static PyObject *Matrix_str(MatrixObject *self) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Rich Compare + * \{ */ + static PyObject *Matrix_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -2298,6 +2338,12 @@ static PyObject *Matrix_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Matrix_hash(MatrixObject *self) { float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2315,16 +2361,22 @@ static Py_hash_t Matrix_hash(MatrixObject *self) return mathutils_array_hash(mat, self->row_num * self->col_num); } -/*---------------------SEQUENCE PROTOCOLS------------------------ - * ----------------------------len(object)------------------------ - * sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Sequence & Mapping Protocol Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Matrix_len(MatrixObject *self) { return self->row_num; } -/*----------------------------object[]--------------------------- - * sequence accessor (get) - * the wrapped vector gives direct access to the matrix data */ + +/** + * Sequence accessor (get): `x = object[i]`. + * \note the wrapped vector gives direct access to the matrix data. + */ static PyObject *Matrix_item_row(MatrixObject *self, int row) { if (BaseMath_ReadCallback_ForWrite(self) == -1) { @@ -2340,7 +2392,10 @@ static PyObject *Matrix_item_row(MatrixObject *self, int row) return Vector_CreatePyObject_cb( (PyObject *)self, self->col_num, mathutils_matrix_row_cb_index, row); } -/* same but column access */ +/** + * Sequence accessor (get): `x = object.col[i]`. + * \note the wrapped vector gives direct access to the matrix data. + */ static PyObject *Matrix_item_col(MatrixObject *self, int col) { if (BaseMath_ReadCallback_ForWrite(self) == -1) { @@ -2357,9 +2412,7 @@ static PyObject *Matrix_item_col(MatrixObject *self, int col) (PyObject *)self, self->row_num, mathutils_matrix_col_cb_index, col); } -/*----------------------------object[]------------------------- - * sequence accessor (set) */ - +/** Sequence accessor (set): `object[i] = x`. */ static int Matrix_ass_item_row(MatrixObject *self, int row, PyObject *value) { int col; @@ -2386,6 +2439,8 @@ static int Matrix_ass_item_row(MatrixObject *self, int row, PyObject *value) (void)BaseMath_WriteCallback(self); return 0; } + +/** Sequence accessor (set): `object.col[i] = x`. */ static int Matrix_ass_item_col(MatrixObject *self, int col, PyObject *value) { int row; @@ -2413,8 +2468,7 @@ static int Matrix_ass_item_col(MatrixObject *self, int col, PyObject *value) return 0; } -/*----------------------------object[z:y]------------------------ - * Sequence slice (get). */ +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Matrix_slice(MatrixObject *self, int begin, int end) { @@ -2439,8 +2493,8 @@ static PyObject *Matrix_slice(MatrixObject *self, int begin, int end) return tuple; } -/*----------------------------object[z:y]------------------------ - * Sequence slice (set). */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Matrix_ass_slice(MatrixObject *self, int begin, int end, PyObject *value) { PyObject *value_fast; @@ -2500,8 +2554,84 @@ static int Matrix_ass_slice(MatrixObject *self, int begin, int end, PyObject *va (void)BaseMath_WriteCallback(self); return 0; } -/*------------------------NUMERIC PROTOCOLS---------------------- - *------------------------obj + obj------------------------------*/ + +/** Sequence generic subscript (get): `x = object[...]`. */ +static PyObject *Matrix_subscript(MatrixObject *self, PyObject *item) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return NULL; + } + if (i < 0) { + i += self->row_num; + } + return Matrix_item_row(self, i); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyTuple_New(0); + } + if (step == 1) { + return Matrix_slice(self, start, stop); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); + return NULL; + } + + PyErr_Format( + PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return NULL; +} + +/** Sequence generic subscript (set): `object[...] = x`. */ +static int Matrix_ass_subscript(MatrixObject *self, PyObject *item, PyObject *value) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return -1; + } + if (i < 0) { + i += self->row_num; + } + return Matrix_ass_item_row(self, i, value); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + if (step == 1) { + return Matrix_ass_slice(self, start, stop, value); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); + return -1; + } + + PyErr_Format( + PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Numeric Protocol Implementation + * \{ */ + +/** Addition: `object + object`. */ static PyObject *Matrix_add(PyObject *m1, PyObject *m2) { float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2534,8 +2664,8 @@ static PyObject *Matrix_add(PyObject *m1, PyObject *m2) return Matrix_CreatePyObject(mat, mat1->col_num, mat1->row_num, Py_TYPE(mat1)); } -/*------------------------obj - obj------------------------------ - * subtraction */ + +/** Subtraction: `object - object`. */ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2) { float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2568,8 +2698,8 @@ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2) return Matrix_CreatePyObject(mat, mat1->col_num, mat1->row_num, Py_TYPE(mat1)); } -/*------------------------obj * obj------------------------------ - * element-wise multiplication */ + +/** Multiplication (element-wise): `object * object`. */ static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar) { float tmat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2631,8 +2761,8 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) Py_TYPE(m2)->tp_name); return NULL; } -/*------------------------obj *= obj------------------------------ - * In place element-wise multiplication */ + +/** Multiplication in-place (element-wise): `object *= object`. */ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2) { float scalar; @@ -2680,8 +2810,8 @@ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2) Py_INCREF(m1); return m1; } -/*------------------------obj @ obj------------------------------ - * matrix multiplication */ + +/** Multiplication (matrix multiply): `object @ object`. */ static PyObject *Matrix_matmul(PyObject *m1, PyObject *m2) { int vec_num; @@ -2756,8 +2886,8 @@ static PyObject *Matrix_matmul(PyObject *m1, PyObject *m2) Py_TYPE(m2)->tp_name); return NULL; } -/*------------------------obj @= obj------------------------------ - * In place matrix multiplication */ + +/** Multiplication in-place (matrix multiply): `object @= object`. */ static PyObject *Matrix_imatmul(PyObject *m1, PyObject *m2) { MatrixObject *mat1 = NULL, *mat2 = NULL; @@ -2816,87 +2946,24 @@ static PyObject *Matrix_imatmul(PyObject *m1, PyObject *m2) return m1; } -/*-----------------PROTOCOL DECLARATIONS--------------------------*/ -static PySequenceMethods Matrix_SeqMethods = { - (lenfunc)Matrix_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Matrix_item_row, /* sq_item */ - (ssizessizeargfunc)NULL, /* sq_slice, deprecated */ - (ssizeobjargproc)Matrix_ass_item_row, /* sq_ass_item */ - (ssizessizeobjargproc)NULL, /* sq_ass_slice, deprecated */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ -}; - -static PyObject *Matrix_subscript(MatrixObject *self, PyObject *item) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i; - i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return NULL; - } - if (i < 0) { - i += self->row_num; - } - return Matrix_item_row(self, i); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { - return NULL; - } - - if (slicelength <= 0) { - return PyTuple_New(0); - } - if (step == 1) { - return Matrix_slice(self, start, stop); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); - return NULL; - } - - PyErr_Format( - PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return NULL; -} - -static int Matrix_ass_subscript(MatrixObject *self, PyObject *item, PyObject *value) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return -1; - } - if (i < 0) { - i += self->row_num; - } - return Matrix_ass_item_row(self, i, value); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; +/** \} */ - if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { - return -1; - } +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Protocol Declarations + * \{ */ - if (step == 1) { - return Matrix_ass_slice(self, start, stop, value); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); - return -1; - } - - PyErr_Format( - PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return -1; -} +static PySequenceMethods Matrix_SeqMethods = { + (lenfunc)Matrix_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Matrix_item_row, /*sq_item*/ + (ssizessizeargfunc)NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Matrix_ass_item_row, /*sq_ass_item*/ + (ssizessizeobjargproc)NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ +}; static PyMappingMethods Matrix_AsMapping = { (lenfunc)Matrix_len, @@ -2924,25 +2991,31 @@ static PyNumberMethods Matrix_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - NULL, /* nb_inplace_add */ - NULL, /* nb_inplace_subtract */ - (binaryfunc)Matrix_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - NULL, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - NULL, /* nb_inplace_true_divide */ - NULL, /* nb_index */ - (binaryfunc)Matrix_matmul, /* nb_matrix_multiply */ - (binaryfunc)Matrix_imatmul, /* nb_inplace_matrix_multiply */ + NULL, /*nb_inplace_add*/ + NULL, /*nb_inplace_subtract*/ + (binaryfunc)Matrix_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + NULL, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + NULL, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ + (binaryfunc)Matrix_matmul, /*nb_matrix_multiply*/ + (binaryfunc)Matrix_imatmul, /*nb_inplace_matrix_multiply*/ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Get/Set Item Implementation + * \{ */ + PyDoc_STRVAR(Matrix_translation_doc, "The translation component of the matrix.\n\n:type: Vector"); static PyObject *Matrix_translation_get(MatrixObject *self, void *UNUSED(closure)) { @@ -3099,9 +3172,12 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void return NULL; } -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Matrix_getseters[] = { {"median_scale", (getter)Matrix_median_scale_get, (setter)NULL, Matrix_median_scale_doc, NULL}, {"translation", @@ -3141,7 +3217,12 @@ static PyGetSetDef Matrix_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/*-----------------------METHOD DEFINITIONS ----------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Method Definitions + * \{ */ + static struct PyMethodDef Matrix_methods[] = { /* Derived values. */ {"determinant", (PyCFunction)Matrix_determinant, METH_NOARGS, Matrix_determinant_doc}, @@ -3205,7 +3286,12 @@ static struct PyMethodDef Matrix_methods[] = { {NULL, NULL, 0, NULL}, }; -/*------------------PY_OBECT DEFINITION--------------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Python Object Definition + * \{ */ + PyDoc_STRVAR( matrix_doc, ".. class:: Matrix([rows])\n" @@ -3268,6 +3354,12 @@ PyTypeObject matrix_Type = { NULL, /*tp_del*/ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: C/API Constructors + * \{ */ + PyObject *Matrix_CreatePyObject(const float *mat, const ushort col_num, const ushort row_num, @@ -3380,6 +3472,12 @@ PyObject *Matrix_CreatePyObject_alloc(float *mat, return (PyObject *)self; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: C/API Parse Utilities + * \{ */ + /** * Use with PyArg_ParseTuple's "O&" formatting. */ @@ -3460,8 +3558,11 @@ int Matrix_Parse4x4(PyObject *o, void *p) return 1; } -/* ---------------------------------------------------------------------------- - * special type for alternate access */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: Struct & Internal Functions + * \{ */ typedef struct { PyObject_HEAD /* Required Python macro. */ @@ -3491,7 +3592,11 @@ static void MatrixAccess_dealloc(MatrixAccessObject *self) Py_TYPE(self)->tp_free(self); } -/* sequence access */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: Sequence Protocol + * \{ */ static int MatrixAccess_len(MatrixAccessObject *self) { @@ -3609,13 +3714,13 @@ static int MatrixAccess_ass_subscript(MatrixAccessObject *self, PyObject *item, static PyObject *MatrixAccess_iter(MatrixAccessObject *self) { - /* Try get values from a collection */ + /* Try get values from a collection. */ PyObject *ret; PyObject *iter = NULL; ret = MatrixAccess_slice(self, 0, MATRIX_MAX_DIM); - /* we know this is a tuple so no need to PyIter_Check - * otherwise it could be NULL (unlikely) if conversion failed */ + /* We know this is a tuple so no need to #PyIter_Check + * otherwise it could be NULL (unlikely) if conversion failed. */ if (ret) { iter = PyObject_GetIter(ret); Py_DECREF(ret); @@ -3630,6 +3735,12 @@ static PyMappingMethods MatrixAccess_AsMapping = { (objobjargproc)MatrixAccess_ass_subscript, }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: Python Object Definition + * \{ */ + PyTypeObject matrix_access_Type = { PyVarObject_HEAD_INIT(NULL, 0) "MatrixAccess", /*tp_name*/ sizeof(MatrixAccessObject), /*tp_basicsize*/ @@ -3658,6 +3769,12 @@ PyTypeObject matrix_access_Type = { (getiterfunc)MatrixAccess_iter, /* getiterfunc tp_iter; */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: C/API Constructor + * \{ */ + static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrixAccess_t type) { MatrixAccessObject *matrix_access = (MatrixAccessObject *)PyObject_GC_New(MatrixObject, @@ -3671,5 +3788,4 @@ static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrix return (PyObject *)matrix_access; } -/* end special access - * -------------------------------------------------------------------------- */ +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Matrix.h b/source/blender/python/mathutils/mathutils_Matrix.h index bc596ce6ac8..c8c207c13f3 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.h +++ b/source/blender/python/mathutils/mathutils_Matrix.h @@ -45,7 +45,8 @@ typedef struct { * be stored in py_data) or be a wrapper for data allocated through * blender (stored in blend_data). This is an either/or struct not both */ -/* prototypes */ +/* Prototypes. */ + PyObject *Matrix_CreatePyObject(const float *mat, ushort col_num, ushort row_num, @@ -70,6 +71,7 @@ PyObject *Matrix_CreatePyObject_alloc(float *mat, PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; /* PyArg_ParseTuple's "O&" formatting helpers. */ + int Matrix_ParseAny(PyObject *o, void *p); int Matrix_Parse2x2(PyObject *o, void *p); int Matrix_Parse3x3(PyObject *o, void *p); diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 7b51154f0d0..6994a313237 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -26,9 +26,49 @@ static void quat__axis_angle_sanitize(float axis[3], float *angle); static PyObject *Quaternion_copy(QuaternionObject *self); static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args); -/* -----------------------------METHODS------------------------------ */ +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ +static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *), + QuaternionObject *self) +{ + PyObject *ret = Quaternion_copy(self); + PyObject *ret_dummy = quat_func((QuaternionObject *)ret); + if (ret_dummy) { + Py_DECREF(ret_dummy); + return ret; + } + /* error */ + Py_DECREF(ret); + return NULL; +} + +/** Axis vector suffers from precision errors, use this function to ensure. */ +static void quat__axis_angle_sanitize(float axis[3], float *angle) +{ + if (axis) { + if (is_zero_v3(axis) || !isfinite(axis[0]) || !isfinite(axis[1]) || !isfinite(axis[2])) { + axis[0] = 1.0f; + axis[1] = 0.0f; + axis[2] = 0.0f; + } + else if (EXPP_FloatsAreEqual(axis[0], 0.0f, 10) && EXPP_FloatsAreEqual(axis[1], 0.0f, 10) && + EXPP_FloatsAreEqual(axis[2], 0.0f, 10)) { + axis[0] = 1.0f; + } + } + + if (angle) { + if (!isfinite(*angle)) { + *angle = 0.0f; + } + } +} + +/** + * \note #BaseMath_ReadCallback must be called beforehand. + */ static PyObject *Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits) { PyObject *ret; @@ -50,6 +90,72 @@ static PyObject *Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: `__new__` / `mathutils.Quaternion()` + * \{ */ + +static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *seq = NULL; + double angle = 0.0f; + float quat[QUAT_SIZE]; + unit_qt(quat); + + if (kwds && PyDict_Size(kwds)) { + PyErr_SetString(PyExc_TypeError, + "mathutils.Quaternion(): " + "takes no keyword args"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle)) { + return NULL; + } + + switch (PyTuple_GET_SIZE(args)) { + case 0: + break; + case 1: { + int size; + + if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == + -1) { + return NULL; + } + + if (size == 4) { + /* 4d: Quaternion (common case) */ + } + else { + /* 3d: Interpret as exponential map */ + BLI_assert(size == 3); + expmap_to_quat(quat, quat); + } + + break; + } + case 2: { + float axis[3]; + if (mathutils_array_parse(axis, 3, 3, seq, "mathutils.Quaternion()") == -1) { + return NULL; + } + angle = angle_wrap_rad(angle); /* clamp because of precision issues */ + axis_angle_to_quat(quat, axis, angle); + break; + /* PyArg_ParseTuple assures no more than 2 */ + } + } + return Quaternion_CreatePyObject(quat, type); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Euler + * \{ */ + PyDoc_STRVAR(Quaternion_to_euler_doc, ".. method:: to_euler(order, euler_compat)\n" "\n" @@ -114,6 +220,12 @@ static PyObject *Quaternion_to_euler(QuaternionObject *self, PyObject *args) return Euler_CreatePyObject(eul, order, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Matrix + * \{ */ + PyDoc_STRVAR(Quaternion_to_matrix_doc, ".. method:: to_matrix()\n" "\n" @@ -133,6 +245,12 @@ static PyObject *Quaternion_to_matrix(QuaternionObject *self) return Matrix_CreatePyObject(mat, 3, 3, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Axis/Angle + * \{ */ + PyDoc_STRVAR(Quaternion_to_axis_angle_doc, ".. method:: to_axis_angle()\n" "\n" @@ -163,6 +281,12 @@ static PyObject *Quaternion_to_axis_angle(QuaternionObject *self) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Swing/Twist + * \{ */ + PyDoc_STRVAR(Quaternion_to_swing_twist_doc, ".. method:: to_swing_twist(axis)\n" "\n" @@ -207,6 +331,12 @@ static PyObject *Quaternion_to_swing_twist(QuaternionObject *self, PyObject *axi return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Exponential Map + * \{ */ + PyDoc_STRVAR( Quaternion_to_exponential_map_doc, ".. method:: to_exponential_map()\n" @@ -232,6 +362,12 @@ static PyObject *Quaternion_to_exponential_map(QuaternionObject *self) return Vector_CreatePyObject(expmap, 3, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Cross Product + * \{ */ + PyDoc_STRVAR(Quaternion_cross_doc, ".. method:: cross(other)\n" "\n" @@ -259,6 +395,12 @@ static PyObject *Quaternion_cross(QuaternionObject *self, PyObject *value) return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Dot Product + * \{ */ + PyDoc_STRVAR(Quaternion_dot_doc, ".. method:: dot(other)\n" "\n" @@ -285,6 +427,12 @@ static PyObject *Quaternion_dot(QuaternionObject *self, PyObject *value) return PyFloat_FromDouble(dot_qtqt(self->quat, tquat)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Rotation Difference + * \{ */ + PyDoc_STRVAR(Quaternion_rotation_difference_doc, ".. function:: rotation_difference(other)\n" "\n" @@ -315,6 +463,12 @@ static PyObject *Quaternion_rotation_difference(QuaternionObject *self, PyObject return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Spherical Interpolation (slerp) + * \{ */ + PyDoc_STRVAR(Quaternion_slerp_doc, ".. function:: slerp(other, factor)\n" "\n" @@ -360,6 +514,12 @@ static PyObject *Quaternion_slerp(QuaternionObject *self, PyObject *args) return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Rotate + * \{ */ + PyDoc_STRVAR(Quaternion_rotate_doc, ".. method:: rotate(other)\n" "\n" @@ -423,9 +583,15 @@ static PyObject *Quaternion_make_compatible(QuaternionObject *self, PyObject *va Py_RETURN_NONE; } -/* ----------------------------Quaternion.normalize()---------------- */ -/* Normalize the quaternion. This may change the angle as well as the - * rotation axis, as all of (w, x, y, z) are scaled. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Normalize + * + * Normalize the quaternion. This may change the angle as well as the + * rotation axis, as all of (w, x, y, z) are scaled. + * \{ */ + PyDoc_STRVAR(Quaternion_normalize_doc, ".. function:: normalize()\n" "\n" @@ -453,6 +619,15 @@ static PyObject *Quaternion_normalized(QuaternionObject *self) return quat__apply_to_copy(Quaternion_normalize, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Invert + * + * Normalize the quaternion. This may change the angle as well as the + * rotation axis, as all of (w, x, y, z) are scaled. + * \{ */ + PyDoc_STRVAR(Quaternion_invert_doc, ".. function:: invert()\n" "\n" @@ -480,6 +655,12 @@ static PyObject *Quaternion_inverted(QuaternionObject *self) return quat__apply_to_copy(Quaternion_invert, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Set Identity + * \{ */ + PyDoc_STRVAR(Quaternion_identity_doc, ".. function:: identity()\n" "\n" @@ -498,6 +679,12 @@ static PyObject *Quaternion_identity(QuaternionObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Negate + * \{ */ + PyDoc_STRVAR(Quaternion_negate_doc, ".. function:: negate()\n" "\n" @@ -516,6 +703,12 @@ static PyObject *Quaternion_negate(QuaternionObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Conjugate + * \{ */ + PyDoc_STRVAR(Quaternion_conjugate_doc, ".. function:: conjugate()\n" "\n" @@ -543,6 +736,12 @@ static PyObject *Quaternion_conjugated(QuaternionObject *self) return quat__apply_to_copy(Quaternion_conjugate, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Copy/Deep-Copy + * \{ */ + PyDoc_STRVAR(Quaternion_copy_doc, ".. function:: copy()\n" "\n" @@ -569,7 +768,12 @@ static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args) return Quaternion_copy(self); } -/* print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: `__repr__` & `__str__` + * \{ */ + static PyObject *Quaternion_repr(QuaternionObject *self) { PyObject *ret, *tuple; @@ -608,6 +812,12 @@ static PyObject *Quaternion_str(QuaternionObject *self) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Rich Compare + * \{ */ + static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -646,6 +856,12 @@ static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Quaternion_hash(QuaternionObject *self) { if (BaseMath_ReadCallback(self) == -1) { @@ -659,15 +875,19 @@ static Py_hash_t Quaternion_hash(QuaternionObject *self) return mathutils_array_hash(self->quat, QUAT_SIZE); } -/* ---------------------SEQUENCE PROTOCOLS------------------------ */ -/* ----------------------------len(object)------------------------ */ -/* sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Sequence & Mapping Protocols Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Quaternion_len(QuaternionObject *UNUSED(self)) { return QUAT_SIZE; } -/* ----------------------------object[]--------------------------- */ -/* sequence accessor (get) */ + +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Quaternion_item(QuaternionObject *self, int i) { if (i < 0) { @@ -687,8 +907,8 @@ static PyObject *Quaternion_item(QuaternionObject *self, int i) return PyFloat_FromDouble(self->quat[i]); } -/* ----------------------------object[]------------------------- */ -/* sequence accessor (set) */ + +/** Sequence accessor (set): `object[i] = x`. */ static int Quaternion_ass_item(QuaternionObject *self, int i, PyObject *ob) { float f; @@ -724,8 +944,8 @@ static int Quaternion_ass_item(QuaternionObject *self, int i, PyObject *ob) return 0; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (get) */ + +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Quaternion_slice(QuaternionObject *self, int begin, int end) { PyObject *tuple; @@ -749,8 +969,8 @@ static PyObject *Quaternion_slice(QuaternionObject *self, int begin, int end) return tuple; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (set) */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyObject *seq) { int i, size; @@ -779,7 +999,7 @@ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyOb return -1; } - /* parsed well - now set in vector */ + /* Parsed well, now set in vector. */ for (i = 0; i < size; i++) { self->quat[begin + i] = quat[i]; } @@ -788,6 +1008,7 @@ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyOb return 0; } +/** Sequence generic subscript (get): `x = object[...]`. */ static PyObject *Quaternion_subscript(QuaternionObject *self, PyObject *item) { if (PyIndex_Check(item)) { @@ -824,6 +1045,7 @@ static PyObject *Quaternion_subscript(QuaternionObject *self, PyObject *item) return NULL; } +/** Sequence generic subscript (set): `object[...] = x`. */ static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyObject *value) { if (PyIndex_Check(item)) { @@ -856,9 +1078,13 @@ static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyOb return -1; } -/* ------------------------NUMERIC PROTOCOLS---------------------- */ -/* ------------------------obj + obj------------------------------ */ -/* addition */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Numeric Protocol Implementation + * \{ */ + +/** Addition: `object + object`. */ static PyObject *Quaternion_add(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; @@ -882,8 +1108,8 @@ static PyObject *Quaternion_add(PyObject *q1, PyObject *q2) add_qt_qtqt(quat, quat1->quat, quat2->quat, 1.0f); return Quaternion_CreatePyObject(quat, Py_TYPE(q1)); } -/* ------------------------obj - obj------------------------------ */ -/* subtraction */ + +/** Subtraction: `object - object`. */ static PyObject *Quaternion_sub(PyObject *q1, PyObject *q2) { int x; @@ -921,8 +1147,7 @@ static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar) return Quaternion_CreatePyObject(tquat, Py_TYPE(quat)); } -/*------------------------obj * obj------------------------------ - * multiplication */ +/** Multiplication (element-wise or scalar): `object * object`. */ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) { float scalar; @@ -965,8 +1190,8 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) Py_TYPE(q2)->tp_name); return NULL; } -/*------------------------obj *= obj------------------------------ - * in-place multiplication */ + +/** Multiplication in-place (element-wise or scalar): `object *= object`. */ static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2) { float scalar; @@ -1005,8 +1230,8 @@ static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2) Py_INCREF(q1); return q1; } -/*------------------------obj @ obj------------------------------ - * quaternion multiplication */ + +/** Multiplication (quaternion multiply): `object @ object`. */ static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; @@ -1060,8 +1285,8 @@ static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2) Py_TYPE(q2)->tp_name); return NULL; } -/*------------------------obj @= obj------------------------------ - * in-place quaternion multiplication */ + +/** Multiplication in-place (quaternion multiply): `object @= object`. */ static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; @@ -1098,8 +1323,7 @@ static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2) return q1; } -/* -obj - * Returns the negative of this object. */ +/** Negative (returns the negative of this object): `-object`. */ static PyObject *Quaternion_neg(QuaternionObject *self) { float tquat[QUAT_SIZE]; @@ -1112,18 +1336,23 @@ static PyObject *Quaternion_neg(QuaternionObject *self) return Quaternion_CreatePyObject(tquat, Py_TYPE(self)); } -/* -----------------PROTOCOL DECLARATIONS-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Protocol Declarations + * \{ */ + static PySequenceMethods Quaternion_SeqMethods = { - (lenfunc)Quaternion_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Quaternion_item, /* sq_item */ - (ssizessizeargfunc)NULL, /* sq_slice, deprecated */ - (ssizeobjargproc)Quaternion_ass_item, /* sq_ass_item */ - (ssizessizeobjargproc)NULL, /* sq_ass_slice, deprecated */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ + (lenfunc)Quaternion_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Quaternion_item, /*sq_item*/ + (ssizessizeargfunc)NULL, /*sq_slice(deprecated)*/ + (ssizeobjargproc)Quaternion_ass_item, /*sq_ass_item*/ + (ssizessizeobjargproc)NULL, /*sq_ass_slice(deprecated)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ }; static PyMappingMethods Quaternion_AsMapping = { @@ -1152,25 +1381,31 @@ static PyNumberMethods Quaternion_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - NULL, /* nb_inplace_add */ - NULL, /* nb_inplace_subtract */ - (binaryfunc)Quaternion_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - NULL, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - NULL, /* nb_inplace_true_divide */ - NULL, /* nb_index */ - (binaryfunc)Quaternion_matmul, /* nb_matrix_multiply */ - (binaryfunc)Quaternion_imatmul, /* nb_inplace_matrix_multiply */ + NULL, /*nb_inplace_add*/ + NULL, /*nb_inplace_subtract*/ + (binaryfunc)Quaternion_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + NULL, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + NULL, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ + (binaryfunc)Quaternion_matmul, /*nb_matrix_multiply*/ + (binaryfunc)Quaternion_imatmul, /*nb_inplace_matrix_multiply*/ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Get/Set Item Implementation + * \{ */ + PyDoc_STRVAR(Quaternion_axis_doc, "Quaternion axis value.\n\n:type: float"); static PyObject *Quaternion_axis_get(QuaternionObject *self, void *type) { @@ -1300,98 +1535,69 @@ static int Quaternion_axis_vector_set(QuaternionObject *self, return 0; } -/* ----------------------------------mathutils.Quaternion() -------------- */ -static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *seq = NULL; - double angle = 0.0f; - float quat[QUAT_SIZE]; - unit_qt(quat); - - if (kwds && PyDict_Size(kwds)) { - PyErr_SetString(PyExc_TypeError, - "mathutils.Quaternion(): " - "takes no keyword args"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle)) { - return NULL; - } - - switch (PyTuple_GET_SIZE(args)) { - case 0: - break; - case 1: { - int size; - - if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == - -1) { - return NULL; - } +/** \} */ - if (size == 4) { - /* 4d: Quaternion (common case) */ - } - else { - /* 3d: Interpret as exponential map */ - BLI_assert(size == 3); - expmap_to_quat(quat, quat); - } - - break; - } - case 2: { - float axis[3]; - if (mathutils_array_parse(axis, 3, 3, seq, "mathutils.Quaternion()") == -1) { - return NULL; - } - angle = angle_wrap_rad(angle); /* clamp because of precision issues */ - axis_angle_to_quat(quat, axis, angle); - break; - /* PyArg_ParseTuple assures no more than 2 */ - } - } - return Quaternion_CreatePyObject(quat, type); -} +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Get/Set Item Definitions + * \{ */ -static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *), - QuaternionObject *self) -{ - PyObject *ret = Quaternion_copy(self); - PyObject *ret_dummy = quat_func((QuaternionObject *)ret); - if (ret_dummy) { - Py_DECREF(ret_dummy); - return ret; - } - /* error */ - Py_DECREF(ret); - return NULL; -} +static PyGetSetDef Quaternion_getseters[] = { + {"w", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)0}, + {"x", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)1}, + {"y", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)2}, + {"z", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)3}, + {"magnitude", (getter)Quaternion_magnitude_get, (setter)NULL, Quaternion_magnitude_doc, NULL}, + {"angle", + (getter)Quaternion_angle_get, + (setter)Quaternion_angle_set, + Quaternion_angle_doc, + NULL}, + {"axis", + (getter)Quaternion_axis_vector_get, + (setter)Quaternion_axis_vector_set, + Quaternion_axis_vector_doc, + NULL}, + {"is_wrapped", + (getter)BaseMathObject_is_wrapped_get, + (setter)NULL, + BaseMathObject_is_wrapped_doc, + NULL}, + {"is_frozen", + (getter)BaseMathObject_is_frozen_get, + (setter)NULL, + BaseMathObject_is_frozen_doc, + NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, + {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, + {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ +}; -/* axis vector suffers from precision errors, use this function to ensure */ -static void quat__axis_angle_sanitize(float axis[3], float *angle) -{ - if (axis) { - if (is_zero_v3(axis) || !isfinite(axis[0]) || !isfinite(axis[1]) || !isfinite(axis[2])) { - axis[0] = 1.0f; - axis[1] = 0.0f; - axis[2] = 0.0f; - } - else if (EXPP_FloatsAreEqual(axis[0], 0.0f, 10) && EXPP_FloatsAreEqual(axis[1], 0.0f, 10) && - EXPP_FloatsAreEqual(axis[2], 0.0f, 10)) { - axis[0] = 1.0f; - } - } +/** \} */ - if (angle) { - if (!isfinite(*angle)) { - *angle = 0.0f; - } - } -} +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Method Definitions + * \{ */ -/* -----------------------METHOD DEFINITIONS ---------------------- */ static struct PyMethodDef Quaternion_methods[] = { /* In place only. */ {"identity", (PyCFunction)Quaternion_identity, METH_NOARGS, Quaternion_identity_doc}, @@ -1446,61 +1652,12 @@ static struct PyMethodDef Quaternion_methods[] = { {NULL, NULL, 0, NULL}, }; -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ -static PyGetSetDef Quaternion_getseters[] = { - {"w", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)0}, - {"x", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)1}, - {"y", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)2}, - {"z", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)3}, - {"magnitude", (getter)Quaternion_magnitude_get, (setter)NULL, Quaternion_magnitude_doc, NULL}, - {"angle", - (getter)Quaternion_angle_get, - (setter)Quaternion_angle_set, - Quaternion_angle_doc, - NULL}, - {"axis", - (getter)Quaternion_axis_vector_get, - (setter)Quaternion_axis_vector_set, - Quaternion_axis_vector_doc, - NULL}, - {"is_wrapped", - (getter)BaseMathObject_is_wrapped_get, - (setter)NULL, - BaseMathObject_is_wrapped_doc, - NULL}, - {"is_frozen", - (getter)BaseMathObject_is_frozen_get, - (setter)NULL, - BaseMathObject_is_frozen_doc, - NULL}, - {"is_valid", - (getter)BaseMathObject_is_valid_get, - (setter)NULL, - BaseMathObject_is_valid_doc, - NULL}, - {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, - {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ -}; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Python Object Definition + * \{ */ -/* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(quaternion_doc, ".. class:: Quaternion([seq, [angle]])\n" "\n" @@ -1577,6 +1734,12 @@ PyTypeObject quaternion_Type = { NULL, /* tp_del */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: C/API Constructors + * \{ */ + PyObject *Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type) { QuaternionObject *self; @@ -1643,3 +1806,5 @@ PyObject *Quaternion_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar c return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Quaternion.h b/source/blender/python/mathutils/mathutils_Quaternion.h index 96e3d2e0055..c45b0a98a7b 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.h +++ b/source/blender/python/mathutils/mathutils_Quaternion.h @@ -20,7 +20,8 @@ typedef struct { * be stored in py_data) or be a wrapper for data allocated through * blender (stored in blend_data). This is an either/or struct not both */ -/* prototypes */ +/* Prototypes. */ + PyObject *Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; PyObject *Quaternion_CreatePyObject_wrap(float quat[4], diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index ffeb121b3d1..0c9cbd6ccfa 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -24,19 +24,108 @@ */ #define MAX_DIMENSIONS 4 -/* Swizzle axes get packed into a single value that is used as a closure. Each +/** + * Swizzle axes get packed into a single value that is used as a closure. Each * axis uses SWIZZLE_BITS_PER_AXIS bits. The first bit (SWIZZLE_VALID_AXIS) is - * used as a sentinel: if it is unset, the axis is not valid. */ + * used as a sentinel: if it is unset, the axis is not valid. + */ #define SWIZZLE_BITS_PER_AXIS 3 #define SWIZZLE_VALID_AXIS 0x4 #define SWIZZLE_AXIS 0x3 static PyObject *Vector_copy(VectorObject *self); static PyObject *Vector_deepcopy(VectorObject *self, PyObject *args); -static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits); + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +/** + * Row vector multiplication - (Vector * Matrix) + * <pre> + * [x][y][z] * [1][4][7] + * [2][5][8] + * [3][6][9] + * </pre> + * \note vector/matrix multiplication is not commutative. + */ static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS], VectorObject *vec, - MatrixObject *mat); + MatrixObject *mat) +{ + float vec_cpy[MAX_DIMENSIONS]; + int row, col, z = 0, vec_num = vec->vec_num; + + if (mat->row_num != vec_num) { + if (mat->row_num == 4 && vec_num == 3) { + vec_cpy[3] = 1.0f; + } + else { + PyErr_SetString(PyExc_ValueError, + "vector * matrix: matrix column size " + "and the vector size must be the same"); + return -1; + } + } + + if (BaseMath_ReadCallback(vec) == -1 || BaseMath_ReadCallback(mat) == -1) { + return -1; + } + + memcpy(vec_cpy, vec->vec, vec_num * sizeof(float)); + + r_vec[3] = 1.0f; + /* Multiplication. */ + for (col = 0; col < mat->col_num; col++) { + double dot = 0.0; + for (row = 0; row < mat->row_num; row++) { + dot += (double)(MATRIX_ITEM(mat, row, col) * vec_cpy[row]); + } + r_vec[z++] = (float)dot; + } + return 0; +} + +static PyObject *vec__apply_to_copy(PyObject *(*vec_func)(VectorObject *), VectorObject *self) +{ + PyObject *ret = Vector_copy(self); + PyObject *ret_dummy = vec_func((VectorObject *)ret); + if (ret_dummy) { + Py_DECREF(ret_dummy); + return (PyObject *)ret; + } + /* error */ + Py_DECREF(ret); + return NULL; +} + +/** \note #BaseMath_ReadCallback must be called beforehand. */ +static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits) +{ + PyObject *ret; + int i; + + ret = PyTuple_New(self->vec_num); + + if (ndigits >= 0) { + for (i = 0; i < self->vec_num; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->vec[i], ndigits))); + } + } + else { + for (i = 0; i < self->vec_num; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->vec[i])); + } + } + + return ret; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: `__new__` / `mathutils.Vector()` + * \{ */ /** * Supports 2D, 3D, and 4D vector objects both int and float values @@ -82,20 +171,12 @@ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return Vector_CreatePyObject_alloc(vec, vec_num, type); } -static PyObject *vec__apply_to_copy(PyObject *(*vec_func)(VectorObject *), VectorObject *self) -{ - PyObject *ret = Vector_copy(self); - PyObject *ret_dummy = vec_func((VectorObject *)ret); - if (ret_dummy) { - Py_DECREF(ret_dummy); - return (PyObject *)ret; - } - /* error */ - Py_DECREF(ret); - return NULL; -} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Class Methods + * \{ */ -/*-----------------------CLASS-METHODS----------------------------*/ PyDoc_STRVAR(C_Vector_Fill_doc, ".. classmethod:: Fill(size, fill=0.0)\n" "\n" @@ -311,7 +392,12 @@ static PyObject *C_Vector_Repeat(PyObject *cls, PyObject *args) return Vector_CreatePyObject_alloc(vec, vec_num, (PyTypeObject *)cls); } -/*-----------------------------METHODS---------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Zero + * \{ */ + PyDoc_STRVAR(Vector_zero_doc, ".. method:: zero()\n" "\n" @@ -331,6 +417,12 @@ static PyObject *Vector_zero(VectorObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Normalize + * \{ */ + PyDoc_STRVAR(Vector_normalize_doc, ".. method:: normalize()\n" "\n" @@ -364,6 +456,12 @@ static PyObject *Vector_normalized(VectorObject *self) return vec__apply_to_copy(Vector_normalize, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Resize + * \{ */ + PyDoc_STRVAR(Vector_resize_doc, ".. method:: resize(size=3)\n" "\n" @@ -553,6 +651,13 @@ static PyObject *Vector_resize_4d(VectorObject *self) self->vec_num = 4; Py_RETURN_NONE; } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: To N-dimensions + * \{ */ + PyDoc_STRVAR(Vector_to_2d_doc, ".. method:: to_2d()\n" "\n" @@ -605,6 +710,12 @@ static PyObject *Vector_to_4d(VectorObject *self) return Vector_CreatePyObject(tvec, 4, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: To Tuple + * \{ */ + PyDoc_STRVAR(Vector_to_tuple_doc, ".. method:: to_tuple(precision=-1)\n" "\n" @@ -614,28 +725,6 @@ PyDoc_STRVAR(Vector_to_tuple_doc, " :type precision: int\n" " :return: the values of the vector rounded by *precision*\n" " :rtype: tuple\n"); -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ -static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits) -{ - PyObject *ret; - int i; - - ret = PyTuple_New(self->vec_num); - - if (ndigits >= 0) { - for (i = 0; i < self->vec_num; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->vec[i], ndigits))); - } - } - else { - for (i = 0; i < self->vec_num; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->vec[i])); - } - } - - return ret; -} - static PyObject *Vector_to_tuple(VectorObject *self, PyObject *args) { int ndigits = 0; @@ -662,6 +751,12 @@ static PyObject *Vector_to_tuple(VectorObject *self, PyObject *args) return Vector_to_tuple_ex(self, ndigits); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: To Track Quaternion + * \{ */ + PyDoc_STRVAR(Vector_to_track_quat_doc, ".. method:: to_track_quat(track, up)\n" "\n" @@ -781,6 +876,12 @@ static PyObject *Vector_to_track_quat(VectorObject *self, PyObject *args) return Quaternion_CreatePyObject(quat, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Orthogonal + * \{ */ + PyDoc_STRVAR( Vector_orthogonal_doc, ".. method:: orthogonal()\n" @@ -816,12 +917,15 @@ static PyObject *Vector_orthogonal(VectorObject *self) return Vector_CreatePyObject(vec, self->vec_num, Py_TYPE(self)); } -/** - * Vector.reflect(mirror): return a reflected vector on the mirror normal. - * <pre> - * vec - ((2 * dot(vec, mirror)) * mirror) - * </pre> - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Reflect + * + * `Vector.reflect(mirror)`: return a reflected vector on the mirror normal: + * `vec - ((2 * dot(vec, mirror)) * mirror)`. + * \{ */ + PyDoc_STRVAR(Vector_reflect_doc, ".. method:: reflect(mirror)\n" "\n" @@ -866,6 +970,12 @@ static PyObject *Vector_reflect(VectorObject *self, PyObject *value) return Vector_CreatePyObject(reflect, self->vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Cross Product + * \{ */ + PyDoc_STRVAR(Vector_cross_doc, ".. method:: cross(other)\n" "\n" @@ -908,6 +1018,12 @@ static PyObject *Vector_cross(VectorObject *self, PyObject *value) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Dot Product + * \{ */ + PyDoc_STRVAR(Vector_dot_doc, ".. method:: dot(other)\n" "\n" @@ -936,6 +1052,12 @@ static PyObject *Vector_dot(VectorObject *self, PyObject *value) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Angle + * \{ */ + PyDoc_STRVAR( Vector_angle_doc, ".. function:: angle(other, fallback=None)\n" @@ -1001,6 +1123,12 @@ static PyObject *Vector_angle(VectorObject *self, PyObject *args) return PyFloat_FromDouble(saacos(dot / (sqrt(dot_self) * sqrt(dot_other)))); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Angle Signed + * \{ */ + PyDoc_STRVAR( Vector_angle_signed_doc, ".. function:: angle_signed(other, fallback)\n" @@ -1055,6 +1183,12 @@ static PyObject *Vector_angle_signed(VectorObject *self, PyObject *args) return PyFloat_FromDouble(angle_signed_v2v2(self->vec, tvec)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Rotation Difference + * \{ */ + PyDoc_STRVAR(Vector_rotation_difference_doc, ".. function:: rotation_difference(other)\n" "\n" @@ -1096,6 +1230,12 @@ static PyObject *Vector_rotation_difference(VectorObject *self, PyObject *value) return Quaternion_CreatePyObject(quat, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Project + * \{ */ + PyDoc_STRVAR(Vector_project_doc, ".. function:: project(other)\n" "\n" @@ -1134,6 +1274,12 @@ static PyObject *Vector_project(VectorObject *self, PyObject *value) return Vector_CreatePyObject_alloc(tvec, vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Linear Interpolation + * \{ */ + PyDoc_STRVAR(Vector_lerp_doc, ".. function:: lerp(other, factor)\n" "\n" @@ -1170,6 +1316,12 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) return Vector_CreatePyObject_alloc(tvec, vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Spherical Interpolation + * \{ */ + PyDoc_STRVAR(Vector_slerp_doc, ".. function:: slerp(other, factor, fallback=None)\n" "\n" @@ -1256,6 +1408,12 @@ static PyObject *Vector_slerp(VectorObject *self, PyObject *args) return Vector_CreatePyObject(ret_vec, vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Rotate + * \{ */ + PyDoc_STRVAR( Vector_rotate_doc, ".. function:: rotate(other)\n" @@ -1297,6 +1455,34 @@ static PyObject *Vector_rotate(VectorObject *self, PyObject *value) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Negate + * \{ */ + +PyDoc_STRVAR(Vector_negate_doc, + ".. method:: negate()\n" + "\n" + " Set all values to their negative.\n"); +static PyObject *Vector_negate(VectorObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) { + return NULL; + } + + negate_vn(self->vec, self->vec_num); + + (void)BaseMath_WriteCallback(self); /* already checked for error */ + Py_RETURN_NONE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Copy/Deep-Copy + * \{ */ + PyDoc_STRVAR(Vector_copy_doc, ".. function:: copy()\n" "\n" @@ -1323,6 +1509,12 @@ static PyObject *Vector_deepcopy(VectorObject *self, PyObject *args) return Vector_copy(self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: `__repr__` & `__str__` + * \{ */ + static PyObject *Vector_repr(VectorObject *self) { PyObject *ret, *tuple; @@ -1362,13 +1554,124 @@ static PyObject *Vector_str(VectorObject *self) } #endif -/* Sequence Protocol */ -/* sequence length len(vector) */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Rich Compare + * \{ */ + +static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) +{ + VectorObject *vecA = NULL, *vecB = NULL; + int result = 0; + const double epsilon = 0.000001f; + double lenA, lenB; + + if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) { + if (comparison_type == Py_NE) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; + } + vecA = (VectorObject *)objectA; + vecB = (VectorObject *)objectB; + + if (BaseMath_ReadCallback(vecA) == -1 || BaseMath_ReadCallback(vecB) == -1) { + return NULL; + } + + if (vecA->vec_num != vecB->vec_num) { + if (comparison_type == Py_NE) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; + } + + switch (comparison_type) { + case Py_LT: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA < lenB) { + result = 1; + } + break; + case Py_LE: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA < lenB) { + result = 1; + } + else { + result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); + } + break; + case Py_EQ: + result = EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); + break; + case Py_NE: + result = !EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); + break; + case Py_GT: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA > lenB) { + result = 1; + } + break; + case Py_GE: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA > lenB) { + result = 1; + } + else { + result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); + } + break; + default: + printf("The result of the comparison could not be evaluated"); + break; + } + if (result == 1) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Hash (`__hash__`) + * \{ */ + +static Py_hash_t Vector_hash(VectorObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) { + return -1; + } + + if (BaseMathObject_Prepare_ForHash(self) == -1) { + return -1; + } + + return mathutils_array_hash(self->vec, self->vec_num); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Sequence & Mapping Protocols Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Vector_len(VectorObject *self) { return self->vec_num; } -/* sequence accessor (get): vector[index] */ + static PyObject *vector_item_internal(VectorObject *self, int i, const bool is_attr) { if (i < 0) { @@ -1395,11 +1698,12 @@ static PyObject *vector_item_internal(VectorObject *self, int i, const bool is_a return PyFloat_FromDouble(self->vec[i]); } +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Vector_item(VectorObject *self, int i) { return vector_item_internal(self, i, false); } -/* sequence accessor (set): vector[index] = value */ + static int vector_ass_item_internal(VectorObject *self, int i, PyObject *value, const bool is_attr) { float scalar; @@ -1442,12 +1746,13 @@ static int vector_ass_item_internal(VectorObject *self, int i, PyObject *value, return 0; } +/** Sequence accessor (set): `object[i] = x`. */ static int Vector_ass_item(VectorObject *self, int i, PyObject *value) { return vector_ass_item_internal(self, i, value, false); } -/* sequence slice (get): vector[a:b] */ +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Vector_slice(VectorObject *self, int begin, int end) { PyObject *tuple; @@ -1471,7 +1776,8 @@ static PyObject *Vector_slice(VectorObject *self, int begin, int end) return tuple; } -/* sequence slice (set): vector[a:b] = value */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *seq) { int vec_num = 0; @@ -1509,8 +1815,83 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se return 0; } -/* Numeric Protocols */ -/* addition: obj + obj */ +/** Sequence generic subscript (get): `x = object[...]`. */ +static PyObject *Vector_subscript(VectorObject *self, PyObject *item) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return NULL; + } + if (i < 0) { + i += self->vec_num; + } + return Vector_item(self, i); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyTuple_New(0); + } + if (step == 1) { + return Vector_slice(self, start, stop); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); + return NULL; + } + + PyErr_Format( + PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return NULL; +} + +/** Sequence generic subscript (set): `object[...] = x`. */ +static int Vector_ass_subscript(VectorObject *self, PyObject *item, PyObject *value) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return -1; + } + if (i < 0) { + i += self->vec_num; + } + return Vector_ass_item(self, i, value); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + if (step == 1) { + return Vector_ass_slice(self, start, stop, value); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); + return -1; + } + + PyErr_Format( + PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Numeric Protocol Implementation + * \{ */ + +/** Addition: `object + object`. */ static PyObject *Vector_add(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1552,7 +1933,7 @@ static PyObject *Vector_add(PyObject *v1, PyObject *v2) return Vector_CreatePyObject_alloc(vec, vec1->vec_num, Py_TYPE(v1)); } -/* addition in-place: obj += obj */ +/** Addition in-place: `object += object`. */ static PyObject *Vector_iadd(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1586,7 +1967,7 @@ static PyObject *Vector_iadd(PyObject *v1, PyObject *v2) return v1; } -/* subtraction: obj - obj */ +/** Subtraction: `object - object`. */ static PyObject *Vector_sub(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1627,7 +2008,7 @@ static PyObject *Vector_sub(PyObject *v1, PyObject *v2) return Vector_CreatePyObject_alloc(vec, vec1->vec_num, Py_TYPE(v1)); } -/* subtraction in-place: obj -= obj */ +/** Subtraction in-place: `object -= object`. */ static PyObject *Vector_isub(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1661,8 +2042,7 @@ static PyObject *Vector_isub(PyObject *v1, PyObject *v2) return v1; } -/*------------------------obj * obj------------------------------ - * multiplication */ +/* Multiply internal implementation `object * object`, `object *= object`. */ int column_vector_multiplication(float r_vec[MAX_DIMENSIONS], VectorObject *vec, MatrixObject *mat) { @@ -1725,6 +2105,7 @@ static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2) return Vector_CreatePyObject_alloc(tvec, vec1->vec_num, Py_TYPE(vec1)); } +/** Multiplication (element-wise or scalar): `object * object`. */ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1776,7 +2157,7 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) return NULL; } -/* multiplication in-place: obj *= obj */ +/** Multiplication in-place (element-wise or scalar): `object *= object`. */ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1830,6 +2211,7 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) return v1; } +/** Multiplication (matrix multiply): `object @ object`. */ static PyObject *Vector_matmul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1893,6 +2275,7 @@ static PyObject *Vector_matmul(PyObject *v1, PyObject *v2) return NULL; } +/** Multiplication in-place (matrix multiply): `object @= object`. */ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2) { PyErr_Format(PyExc_TypeError, @@ -1903,7 +2286,7 @@ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2) return NULL; } -/* divide: obj / obj */ +/** Division: `object / object`. */ static PyObject *Vector_div(PyObject *v1, PyObject *v2) { float *vec = NULL, scalar; @@ -1950,7 +2333,7 @@ static PyObject *Vector_div(PyObject *v1, PyObject *v2) return Vector_CreatePyObject_alloc(vec, vec1->vec_num, Py_TYPE(v1)); } -/* divide in-place: obj /= obj */ +/** Division in-place: `object /= object`. */ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) { float scalar; @@ -1983,8 +2366,7 @@ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) return v1; } -/* -obj - * Returns the negative of this object. */ +/** Negative (returns the negative of this object): `-object`. */ static PyObject *Vector_neg(VectorObject *self) { float *tvec; @@ -1998,184 +2380,25 @@ static PyObject *Vector_neg(VectorObject *self) return Vector_CreatePyObject_alloc(tvec, self->vec_num, Py_TYPE(self)); } -/*------------------------tp_richcmpr - * returns -1 exception, 0 false, 1 true */ -static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) -{ - VectorObject *vecA = NULL, *vecB = NULL; - int result = 0; - const double epsilon = 0.000001f; - double lenA, lenB; - - if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) { - if (comparison_type == Py_NE) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; - } - vecA = (VectorObject *)objectA; - vecB = (VectorObject *)objectB; - - if (BaseMath_ReadCallback(vecA) == -1 || BaseMath_ReadCallback(vecB) == -1) { - return NULL; - } +/** \} */ - if (vecA->vec_num != vecB->vec_num) { - if (comparison_type == Py_NE) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; - } - - switch (comparison_type) { - case Py_LT: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA < lenB) { - result = 1; - } - break; - case Py_LE: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA < lenB) { - result = 1; - } - else { - result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); - } - break; - case Py_EQ: - result = EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); - break; - case Py_NE: - result = !EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); - break; - case Py_GT: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA > lenB) { - result = 1; - } - break; - case Py_GE: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA > lenB) { - result = 1; - } - else { - result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); - } - break; - default: - printf("The result of the comparison could not be evaluated"); - break; - } - if (result == 1) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static Py_hash_t Vector_hash(VectorObject *self) -{ - if (BaseMath_ReadCallback(self) == -1) { - return -1; - } +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Protocol Declarations + * \{ */ - if (BaseMathObject_Prepare_ForHash(self) == -1) { - return -1; - } - - return mathutils_array_hash(self->vec, self->vec_num); -} - -/*-----------------PROTCOL DECLARATIONS--------------------------*/ static PySequenceMethods Vector_SeqMethods = { - (lenfunc)Vector_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Vector_item, /* sq_item */ - NULL, /* py3 deprecated slice func */ - (ssizeobjargproc)Vector_ass_item, /* sq_ass_item */ - NULL, /* py3 deprecated slice assign func */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ + (lenfunc)Vector_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Vector_item, /*sq_item*/ + NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Vector_ass_item, /*sq_ass_item*/ + NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat */ + (ssizeargfunc)NULL, /*sq_inplace_repeat */ }; -static PyObject *Vector_subscript(VectorObject *self, PyObject *item) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i; - i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return NULL; - } - if (i < 0) { - i += self->vec_num; - } - return Vector_item(self, i); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { - return NULL; - } - - if (slicelength <= 0) { - return PyTuple_New(0); - } - if (step == 1) { - return Vector_slice(self, start, stop); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); - return NULL; - } - - PyErr_Format( - PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return NULL; -} - -static int Vector_ass_subscript(VectorObject *self, PyObject *item, PyObject *value) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return -1; - } - if (i < 0) { - i += self->vec_num; - } - return Vector_ass_item(self, i, value); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { - return -1; - } - - if (step == 1) { - return Vector_ass_slice(self, start, stop, value); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); - return -1; - } - - PyErr_Format( - PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return -1; -} - static PyMappingMethods Vector_AsMapping = { (lenfunc)Vector_len, (binaryfunc)Vector_subscript, @@ -2202,28 +2425,32 @@ static PyNumberMethods Vector_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - Vector_iadd, /* nb_inplace_add */ - Vector_isub, /* nb_inplace_subtract */ - Vector_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - Vector_div, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - Vector_idiv, /* nb_inplace_true_divide */ - NULL, /* nb_index */ - (binaryfunc)Vector_matmul, /* nb_matrix_multiply */ - (binaryfunc)Vector_imatmul, /* nb_inplace_matrix_multiply */ + Vector_iadd, /*nb_inplace_add*/ + Vector_isub, /*nb_inplace_subtract*/ + Vector_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + Vector_div, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + Vector_idiv, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ + (binaryfunc)Vector_matmul, /*nb_matrix_multiply*/ + (binaryfunc)Vector_imatmul, /*nb_inplace_matrix_multiply*/ }; -/*------------------PY_OBECT DEFINITION--------------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Get/Set Item Implementation + * \{ */ -/* vector axis, vector.x/y/z/w */ +/* Vector axis: `vector.x/y/z/w`. */ PyDoc_STRVAR(Vector_axis_x_doc, "Vector X axis.\n\n:type: float"); PyDoc_STRVAR(Vector_axis_y_doc, "Vector Y axis.\n\n:type: float"); @@ -2240,7 +2467,7 @@ static int Vector_axis_set(VectorObject *self, PyObject *value, void *type) return vector_ass_item_internal(self, POINTER_AS_INT(type), value, true); } -/* vector.length */ +/* `Vector.length`. */ PyDoc_STRVAR(Vector_length_doc, "Vector Length.\n\n:type: float"); static PyObject *Vector_length_get(VectorObject *self, void *UNUSED(closure)) @@ -2296,7 +2523,7 @@ static int Vector_length_set(VectorObject *self, PyObject *value) return 0; } -/* vector.length_squared */ +/* `Vector.length_squared`. */ PyDoc_STRVAR(Vector_length_squared_doc, "Vector length squared (v.dot(v)).\n\n:type: float"); static PyObject *Vector_length_squared_get(VectorObject *self, void *UNUSED(closure)) { @@ -2503,9 +2730,12 @@ static int Vector_swizzle_set(VectorObject *self, PyObject *value, void *closure #define SWIZZLE3(a, b, c) POINTER_FROM_INT(_SWIZZLE3(a, b, c)) #define SWIZZLE4(a, b, c, d) POINTER_FROM_INT(_SWIZZLE4(a, b, c, d)) -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Vector_getseters[] = { {"x", (getter)Vector_axis_get, (setter)Vector_axis_set, Vector_axis_x_doc, (void *)0}, {"y", (getter)Vector_axis_get, (setter)Vector_axis_set, Vector_axis_y_doc, (void *)1}, @@ -2886,68 +3116,11 @@ static PyGetSetDef Vector_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/** - * Row vector multiplication - (Vector * Matrix) - * <pre> - * [x][y][z] * [1][4][7] - * [2][5][8] - * [3][6][9] - * </pre> - * \note vector/matrix multiplication is not commutative. - */ -static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS], - VectorObject *vec, - MatrixObject *mat) -{ - float vec_cpy[MAX_DIMENSIONS]; - int row, col, z = 0, vec_num = vec->vec_num; - - if (mat->row_num != vec_num) { - if (mat->row_num == 4 && vec_num == 3) { - vec_cpy[3] = 1.0f; - } - else { - PyErr_SetString(PyExc_ValueError, - "vector * matrix: matrix column size " - "and the vector size must be the same"); - return -1; - } - } - - if (BaseMath_ReadCallback(vec) == -1 || BaseMath_ReadCallback(mat) == -1) { - return -1; - } - - memcpy(vec_cpy, vec->vec, vec_num * sizeof(float)); - - r_vec[3] = 1.0f; - /* Multiplication. */ - for (col = 0; col < mat->col_num; col++) { - double dot = 0.0; - for (row = 0; row < mat->row_num; row++) { - dot += (double)(MATRIX_ITEM(mat, row, col) * vec_cpy[row]); - } - r_vec[z++] = (float)dot; - } - return 0; -} - -/*----------------------------Vector.negate() -------------------- */ -PyDoc_STRVAR(Vector_negate_doc, - ".. method:: negate()\n" - "\n" - " Set all values to their negative.\n"); -static PyObject *Vector_negate(VectorObject *self) -{ - if (BaseMath_ReadCallback(self) == -1) { - return NULL; - } - - negate_vn(self->vec, self->vec_num); +/** \} */ - (void)BaseMath_WriteCallback(self); /* already checked for error */ - Py_RETURN_NONE; -} +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Method Definitions + * \{ */ static struct PyMethodDef Vector_methods[] = { /* Class Methods */ @@ -3000,11 +3173,15 @@ static struct PyMethodDef Vector_methods[] = { {NULL, NULL, 0, NULL}, }; -/** - * NOTE: #Py_TPFLAGS_CHECKTYPES allows us to avoid casting all types to Vector when coercing +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Python Object Definition + * + * \note #Py_TPFLAGS_CHECKTYPES allows us to avoid casting all types to Vector when coercing * but this means for eg that (vec * mat) and (mat * vec) * both get sent to Vector_mul and it needs to sort out the order - */ + * \{ */ PyDoc_STRVAR(vector_doc, ".. class:: Vector(seq)\n" @@ -3098,6 +3275,12 @@ PyTypeObject vector_Type = { NULL, }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: C/API Constructors + * \{ */ + PyObject *Vector_CreatePyObject(const float *vec, const int vec_num, PyTypeObject *base_type) { VectorObject *self; @@ -3190,3 +3373,5 @@ PyObject *Vector_CreatePyObject_alloc(float *vec, const int vec_num, PyTypeObjec return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Vector.h b/source/blender/python/mathutils/mathutils_Vector.h index 3bc4e9d6b6f..7006ece6bf0 100644 --- a/source/blender/python/mathutils/mathutils_Vector.h +++ b/source/blender/python/mathutils/mathutils_Vector.h @@ -18,7 +18,8 @@ typedef struct { int vec_num; } VectorObject; -/*prototypes*/ +/* Prototypes. */ + PyObject *Vector_CreatePyObject(const float *vec, int vec_num, PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index faa527786fd..ad57412034a 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -129,7 +129,13 @@ Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int seq->pitch = 1.0f; seq->scene_sound = NULL; seq->type = type; - seq->blend_mode = SEQ_TYPE_ALPHAOVER; + + if (seq->type == SEQ_TYPE_ADJUSTMENT) { + seq->blend_mode = SEQ_TYPE_CROSS; + } + else { + seq->blend_mode = SEQ_TYPE_ALPHAOVER; + } seq->strip = seq_strip_alloc(type); seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 59d40ce840f..dda60975a96 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -770,6 +770,41 @@ void WM_operator_properties_filesel(struct wmOperatorType *ot, eFileSel_Flag flag, short display, short sort); + +/** + * Tries to pass \a id to an operator via either a "session_uuid" or a "name" property defined in + * the properties of \a ptr. The former is preferred, since it works properly with linking and + * library overrides (which may both result in multiple IDs with the same name and type). + * + * Also see #WM_operator_properties_id_lookup() and + * #WM_operator_properties_id_lookup_from_name_or_session_uuid() + */ +void WM_operator_properties_id_lookup_set_from_id(PointerRNA *ptr, const ID *id); +/** + * Tries to find an ID in \a bmain. There needs to be either a "session_uuid" int or "name" string + * property defined and set. The former has priority. See #WM_operator_properties_id_lookup() for a + * helper to add the properties. + */ +struct ID *WM_operator_properties_id_lookup_from_name_or_session_uuid(struct Main *bmain, + PointerRNA *ptr, + enum ID_Type type); +/** + * Check if either the "session_uuid" or "name" property is set inside \a ptr. If this is the case + * the ID can be looked up by #WM_operator_properties_id_lookup_from_name_or_session_uuid(). + */ +bool WM_operator_properties_id_lookup_is_set(PointerRNA *ptr); +/** + * Adds "name" and "session_uuid" properties so the caller can tell the operator which ID to act + * on. See #WM_operator_properties_id_lookup_from_name_or_session_uuid(). Both properties will be + * hidden in the UI and not be saved over consecutive operator calls. + * + * \note New operators should probably use "session_uuid" only (set \a add_name_prop to #false), + * since this works properly with linked data and/or library overrides (in both cases, multiple IDs + * with the same name and type may be present). The "name" property is only kept to not break + * compatibility with old scripts using some previously existing operators. + */ +void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop); + /** * Disable using cursor position, * use when view operators are initialized from buttons. @@ -939,6 +974,14 @@ bool WM_operatortype_remove(const char *idname); * Remove memory of all previously executed tools. */ void WM_operatortype_last_properties_clear_all(void); + +void WM_operatortype_idname_visit_for_search(const struct bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /** * Tag all operator-properties of \a ot defined after calling this, until * the next #WM_operatortype_props_advanced_end call (if available), with @@ -1042,6 +1085,13 @@ void WM_menutype_freelink(struct MenuType *mt); void WM_menutype_free(void); bool WM_menutype_poll(struct bContext *C, struct MenuType *mt); +void WM_menutype_idname_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /* wm_panel_type.c */ /** @@ -1053,6 +1103,13 @@ struct PanelType *WM_paneltype_find(const char *idname, bool quiet); bool WM_paneltype_add(struct PanelType *pt); void WM_paneltype_remove(struct PanelType *pt); +void WM_paneltype_idname_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /* wm_gesture_ops.c */ int WM_gesture_box_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index a98ded82a92..8f96080c810 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -683,15 +683,10 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * return; } - /* Get name from property, not asset data - it may have changed after importing to ensure - * uniqueness (name is assumed to be set from the imported ID name). */ - char name[MAX_ID_NAME - 2]; - RNA_string_get(drop->ptr, "name", name); - if (!name[0]) { - return; - } - - ID *id = BKE_libblock_find_name(bmain, asset_drag->id_type, name); + /* Try to find the imported ID. For this to work either a "session_uuid" or "name" property must + * have been defined (see #WM_operator_properties_id_lookup()). */ + ID *id = WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, drop->ptr, asset_drag->id_type); if (id != NULL) { /* Do not delete the dragged ID if it has any user, otherwise if it is a 're-used' ID it will * cause T95636. Note that we need first to add the user that we want to remove in diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 1fdc8bbe2c8..e3892933905 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -819,6 +819,7 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev /* Toggle flipping on/off. */ gesture->use_flip = !gesture->use_flip; gesture_straightline_apply(C, op); + wm_gesture_tag_redraw(win); break; } case GESTURE_MODAL_SELECT: { @@ -897,6 +898,7 @@ int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmE case GESTURE_MODAL_FLIP: { /* Toggle flip on/off. */ gesture->use_flip = !gesture->use_flip; + wm_gesture_tag_redraw(win); break; } case GESTURE_MODAL_SELECT: diff --git a/source/blender/windowmanager/intern/wm_menu_type.c b/source/blender/windowmanager/intern/wm_menu_type.c index 9e50ebb1ce5..b4cf5a79cfa 100644 --- a/source/blender/windowmanager/intern/wm_menu_type.c +++ b/source/blender/windowmanager/intern/wm_menu_type.c @@ -99,3 +99,21 @@ bool WM_menutype_poll(bContext *C, MenuType *mt) } return true; } + +void WM_menutype_idname_visit_for_search(const bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, menutypes_hash) { + MenuType *mt = BLI_ghashIterator_getValue(&gh_iter); + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = mt->idname; + visit_params.info = mt->label; + visit_fn(visit_user_data, &visit_params); + } +} diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 6e44246f3ef..71c948dfbb9 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -8,8 +8,12 @@ * (`WM_operator_properties_*` functions). */ +#include "DNA_ID_enums.h" #include "DNA_space_types.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" + #include "BLI_math_base.h" #include "BLI_rect.h" @@ -222,6 +226,74 @@ void WM_operator_properties_filesel(wmOperatorType *ot, RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +void WM_operator_properties_id_lookup_set_from_id(PointerRNA *ptr, const ID *id) +{ + PropertyRNA *prop_session_uuid = RNA_struct_find_property(ptr, "session_uuid"); + PropertyRNA *prop_name = RNA_struct_find_property(ptr, "name"); + + if (prop_session_uuid) { + RNA_int_set(ptr, "session_uuid", (int)id->session_uuid); + } + else if (prop_name) { + RNA_string_set(ptr, "name", id->name + 2); + } + else { + BLI_assert_unreachable(); + } +} + +ID *WM_operator_properties_id_lookup_from_name_or_session_uuid(Main *bmain, + PointerRNA *ptr, + const ID_Type type) +{ + PropertyRNA *prop_session_uuid = RNA_struct_find_property(ptr, "session_uuid"); + if (prop_session_uuid && RNA_property_is_set(ptr, prop_session_uuid)) { + const uint32_t session_uuid = (uint32_t)RNA_property_int_get(ptr, prop_session_uuid); + return BKE_libblock_find_session_uuid(bmain, type, session_uuid); + } + + PropertyRNA *prop_name = RNA_struct_find_property(ptr, "name"); + if (prop_name && RNA_property_is_set(ptr, prop_name)) { + char name[MAX_ID_NAME - 2]; + RNA_property_string_get(ptr, prop_name, name); + return BKE_libblock_find_name(bmain, type, name); + } + + return NULL; +} + +bool WM_operator_properties_id_lookup_is_set(PointerRNA *ptr) +{ + return RNA_struct_property_is_set(ptr, "session_uuid") || + RNA_struct_property_is_set(ptr, "name"); +} + +void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop) +{ + PropertyRNA *prop; + + if (add_name_prop) { + prop = RNA_def_string(ot->srna, + "name", + NULL, + MAX_ID_NAME - 2, + "Name", + "Name of the data-block to use by the operator"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + } + + prop = RNA_def_int(ot->srna, + "session_uuid", + 0, + INT32_MIN, + INT32_MAX, + "Session UUID", + "Session UUID of the data-block to use by the operator", + INT32_MIN, + INT32_MAX); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); +} + static void wm_operator_properties_select_action_ex(wmOperatorType *ot, int default_action, const EnumPropertyItem *select_actions, diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 8aa8469f0bc..d1c27504628 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -246,6 +246,27 @@ void WM_operatortype_last_properties_clear_all(void) } } +void WM_operatortype_idname_visit_for_search(const bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, global_ops_hash) { + wmOperatorType *ot = BLI_ghashIterator_getValue(&gh_iter); + + char idname_py[OP_MAX_TYPENAME]; + WM_operator_py_idname(idname_py, ot->idname); + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = idname_py; + visit_params.info = ot->name; + visit_fn(visit_user_data, &visit_params); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index f455f7f2719..307d3282659 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1266,6 +1266,7 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i { Main *bmain = CTX_data_main(C); ID *id = NULL; + /* check input variables */ if (RNA_struct_property_is_set(op->ptr, "filepath")) { const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); @@ -1303,19 +1304,29 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i } } } + + return id; } - else if (RNA_struct_property_is_set(op->ptr, "name")) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - id = BKE_libblock_find_name(bmain, idcode, name); - if (!id) { + + /* Lookup an already existing ID. */ + id = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, idcode); + + if (!id) { + /* Print error with the name if the name is available. */ + + if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); BKE_reportf( op->reports, RPT_ERROR, "%s '%s' not found", BKE_idtype_idcode_to_name(idcode), name); return NULL; } - id_us_plus(id); + + BKE_reportf(op->reports, RPT_ERROR, "%s not found", BKE_idtype_idcode_to_name(idcode)); + return NULL; } + id_us_plus(id); return id; } @@ -1859,7 +1870,14 @@ static void WM_OT_call_menu(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + RNA_def_property_string_search_func_runtime( + prop, + WM_menutype_idname_visit_for_search, + /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */ + (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION)); } static int wm_call_pie_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -1891,7 +1909,14 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu"); + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu"); + RNA_def_property_string_search_func_runtime( + prop, + WM_menutype_idname_visit_for_search, + /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */ + (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION)); } static int wm_call_panel_exec(bContext *C, wmOperator *op) @@ -1927,6 +1952,11 @@ static void WM_OT_call_panel(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + RNA_def_property_string_search_func_runtime( + prop, + WM_paneltype_idname_visit_for_search, + /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */ + (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION)); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/windowmanager/intern/wm_panel_type.c b/source/blender/windowmanager/intern/wm_panel_type.c index bfcc86c38e7..860b53c1071 100644 --- a/source/blender/windowmanager/intern/wm_panel_type.c +++ b/source/blender/windowmanager/intern/wm_panel_type.c @@ -65,3 +65,21 @@ void WM_paneltype_clear(void) { BLI_ghash_free(g_paneltypes_hash, NULL, NULL); } + +void WM_paneltype_idname_visit_for_search(const bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, g_paneltypes_hash) { + PanelType *pt = BLI_ghashIterator_getValue(&gh_iter); + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = pt->idname; + visit_params.info = pt->label; + visit_fn(visit_user_data, &visit_params); + } +} diff --git a/source/tools b/source/tools -Subproject 53b7c02a062c3d6ec6b38ce670836321b4e78fa +Subproject ccc8fceb6bd83ffbf6e5207247fb8f76fc47a5b |