diff options
author | Jeroen Bakker <jeroen@blender.org> | 2022-05-11 11:35:58 +0300 |
---|---|---|
committer | Jeroen Bakker <jeroen@blender.org> | 2022-05-11 11:35:58 +0300 |
commit | c8f3ec797c66e834cd6c481e36665ec80add6169 (patch) | |
tree | f0d31c2cbd557dbc2de00df3e17d66ebc9decbc3 | |
parent | 2e046183a2a9cb8cc4477d4cecbe13312fe8cd41 (diff) | |
parent | b30cb05c14a9061f53367e9a4ad76d39dc62d7ee (diff) |
Merge branch 'master' into temp-T97352-3d-texturing-seam-bleeding
504 files changed, 10352 insertions, 4407 deletions
diff --git a/.clang-tidy b/.clang-tidy index 1cc0e6e7f4a..42ce52d58ca 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,8 @@ # The warnings below are disabled because they are too pedantic and not worth fixing. # Some of them will be enabled as part of the Clang-Tidy task, see T78535. +# NOTE: No comments in the list below is allowed. Clang-tidy will ignore items after comments in the lists flag list. +# This is because the comment is not a valid list item and it will stop parsing flags if a list item is a comment. Checks: > -*, readability-*, @@ -14,10 +16,9 @@ Checks: > -readability-make-member-function-const, -readability-suspicious-call-argument, -readability-redundant-member-init, - -readability-misleading-indentation, - -readability-use-anyofallof, + -readability-identifier-length, -readability-function-cognitive-complexity, @@ -35,6 +36,8 @@ Checks: > -bugprone-redundant-branch-condition, + -bugprone-suspicious-include, + modernize-*, -modernize-use-auto, -modernize-use-trailing-return-type, @@ -42,8 +45,6 @@ Checks: > -modernize-use-nodiscard, -modernize-loop-convert, -modernize-pass-by-value, - # Cannot be enabled yet, because using raw string literals in tests breaks - # the windows compiler currently. -modernize-raw-string-literal, -modernize-return-braced-init-list diff --git a/CMakeLists.txt b/CMakeLists.txt index 06e9fba4aa9..3e038fe3edd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -486,7 +486,7 @@ if((UNIX AND NOT APPLE) OR (CMAKE_GENERATOR MATCHES "^Visual Studio.+")) endif() option(WITH_BOOST "Enable features depending on boost" ON) -option(WITH_TBB "Enable features depending on TBB (OpenVDB, OpenImageDenoise, sculpt multithreading)" ON) +option(WITH_TBB "Enable multithreading. TBB is also required for features such as Cycles, OpenVDB and USD" ON) # TBB malloc is only supported on for windows currently if(WIN32) @@ -781,7 +781,9 @@ set_and_warn_dependency(WITH_BOOST WITH_OPENCOLORIO OFF) set_and_warn_dependency(WITH_BOOST WITH_QUADRIFLOW OFF) set_and_warn_dependency(WITH_BOOST WITH_USD OFF) set_and_warn_dependency(WITH_BOOST WITH_ALEMBIC OFF) -set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF) +if(WITH_CYCLES) + set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF) +endif() set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF) if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR @@ -1539,7 +1541,6 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ALL -Wall) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_CAST_ALIGN -Wcast-align) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_VLA -Werror=vla) diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 8f4738d1f1c..b63e86a3ac2 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -54,9 +54,6 @@ include(cmake/freetype.cmake) include(cmake/freeglut.cmake) include(cmake/glew.cmake) include(cmake/alembic.cmake) -include(cmake/glfw.cmake) -include(cmake/clew.cmake) -include(cmake/cuew.cmake) include(cmake/opensubdiv.cmake) include(cmake/sdl.cmake) include(cmake/opencollada.cmake) diff --git a/build_files/build_environment/cmake/clew.cmake b/build_files/build_environment/cmake/clew.cmake deleted file mode 100644 index 8c965d52d2c..00000000000 --- a/build_files/build_environment/cmake/clew.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(CLEW_EXTRA_ARGS) - -ExternalProject_Add(external_clew - URL file://${PACKAGE_DIR}/${CLEW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${CLEW_HASH_TYPE}=${CLEW_HASH} - PREFIX ${BUILD_DIR}/clew - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/clew -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${CLEW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/clew -) diff --git a/build_files/build_environment/cmake/cuew.cmake b/build_files/build_environment/cmake/cuew.cmake deleted file mode 100644 index 8beb8b4fade..00000000000 --- a/build_files/build_environment/cmake/cuew.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(CUEW_EXTRA_ARGS) - -ExternalProject_Add(external_cuew - URL file://${PACKAGE_DIR}/${CUEW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${CUEW_HASH_TYPE}=${CUEW_HASH} - PREFIX ${BUILD_DIR}/cuew - PATCH_COMMAND ${PATCH_CMD} --verbose -p 0 -N -d ${BUILD_DIR}/cuew/src/external_cuew < ${PATCH_DIR}/cuew.diff - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/cuew -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${CUEW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/cuew -) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index 80c53d05c28..81e7f7ab3fe 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -39,10 +39,6 @@ download_source(FREETYPE) download_source(GLEW) download_source(FREEGLUT) download_source(ALEMBIC) -download_source(GLFW) -download_source(CLEW) -download_source(GLFW) -download_source(CUEW) download_source(OPENSUBDIV) download_source(SDL) download_source(OPENCOLLADA) diff --git a/build_files/build_environment/cmake/glfw.cmake b/build_files/build_environment/cmake/glfw.cmake deleted file mode 100644 index 29fce7609e5..00000000000 --- a/build_files/build_environment/cmake/glfw.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(GLFW_EXTRA_ARGS) - -ExternalProject_Add(external_glfw - URL file://${PACKAGE_DIR}/${GLFW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${GLFW_HASH_TYPE}=${GLFW_HASH} - PREFIX ${BUILD_DIR}/glfw - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/glfw -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${GLFW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/glfw -) diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake index 2246031be83..4e2d1503406 100644 --- a/build_files/build_environment/cmake/opencolorio.cmake +++ b/build_files/build_environment/cmake/opencolorio.cmake @@ -53,7 +53,8 @@ add_dependencies( external_opencolorio external_yamlcpp external_expat - external_openexr + external_imath + external_pystring ) if(WIN32) diff --git a/build_files/build_environment/cmake/opensubdiv.cmake b/build_files/build_environment/cmake/opensubdiv.cmake index 6d6e3568406..a142160b9cc 100644 --- a/build_files/build_environment/cmake/opensubdiv.cmake +++ b/build_files/build_environment/cmake/opensubdiv.cmake @@ -10,20 +10,16 @@ set(OPENSUBDIV_EXTRA_ARGS -DNO_OMP=ON -DNO_TBB=OFF -DNO_CUDA=ON - -DNO_OPENCL=OFF - -DNO_CLEW=OFF + -DNO_OPENCL=ON + -DNO_CLEW=ON -DNO_OPENGL=OFF -DNO_METAL=OFF -DNO_DX=ON -DNO_TESTS=ON -DNO_GLTESTS=ON - -DNO_GLEW=OFF - -DNO_GLFW=OFF + -DNO_GLEW=ON + -DNO_GLFW=ON -DNO_GLFW_X11=ON - -DGLEW_INCLUDE_DIR=${LIBDIR}/glew/include - -DGLEW_LIBRARY=${LIBDIR}/glew/lib/libGLEW${LIBEXT} - -DGLFW_INCLUDE_DIR=${LIBDIR}/glfw/include - -DGLFW_LIBRARIES=${LIBDIR}/glfw/lib/glfw3${LIBEXT} ) if(WIN32) @@ -31,19 +27,12 @@ if(WIN32) ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/tbb.lib - -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL - -DCLEW_LIBRARY=${LIBDIR}/clew/lib/clew${LIBEXT} - -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include - -DCUEW_LIBRARY=${LIBDIR}/cuew/lib/cuew${LIBEXT} ) else() set(OPENSUBDIV_EXTRA_ARGS ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include -DTBB_tbb_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}tbb_static${LIBEXT} - -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include - -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL - -DCLEW_LIBRARY=${LIBDIR}/clew/lib/static/${LIBPREFIX}clew${LIBEXT} ) endif() @@ -75,9 +64,5 @@ endif() add_dependencies( external_opensubdiv - external_glew - external_glfw - external_clew - external_cuew external_tbb ) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 25267273db0..550be86b6b6 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -98,27 +98,6 @@ set(ALEMBIC_HASH 2cd8d6e5a3ac4a014e24a4b04f4fadf9) set(ALEMBIC_HASH_TYPE MD5) set(ALEMBIC_FILE alembic-${ALEMBIC_VERSION}.tar.gz) -# hash is for 3.1.2 -set(GLFW_GIT_UID 30306e54705c3adae9fe082c816a3be71963485c) -set(GLFW_URI https://github.com/glfw/glfw/archive/${GLFW_GIT_UID}.zip) -set(GLFW_HASH 20cacb1613da7eeb092f3ac4f6b2b3d0) -set(GLFW_HASH_TYPE MD5) -set(GLFW_FILE glfw-${GLFW_GIT_UID}.zip) - -# latest uid in git as of 2016-04-01 -set(CLEW_GIT_UID 277db43f6cafe8b27c6f1055f69dc67da4aeb299) -set(CLEW_URI https://github.com/OpenCLWrangler/clew/archive/${CLEW_GIT_UID}.zip) -set(CLEW_HASH 2c699d10ed78362e71f56fae2a4c5f98) -set(CLEW_HASH_TYPE MD5) -set(CLEW_FILE clew-${CLEW_GIT_UID}.zip) - -# latest uid in git as of 2016-04-01 -set(CUEW_GIT_UID 1744972026de9cf27c8a7dc39cf39cd83d5f922f) -set(CUEW_URI https://github.com/CudaWrangler/cuew/archive/${CUEW_GIT_UID}.zip) -set(CUEW_HASH 86760d62978ebfd96cd93f5aa1abaf4a) -set(CUEW_HASH_TYPE MD5) -set(CUEW_FILE cuew-${CUEW_GIT_UID}.zip) - set(OPENSUBDIV_VERSION v3_4_4) set(OPENSUBDIV_URI https://github.com/PixarAnimationStudios/OpenSubdiv/archive/${OPENSUBDIV_VERSION}.tar.gz) set(OPENSUBDIV_HASH 39ecc5caf0abebc943d1ce131855e76e) diff --git a/build_files/build_environment/dependencies.dot b/build_files/build_environment/dependencies.dot index 62949f3de62..7e8637fbced 100644 --- a/build_files/build_environment/dependencies.dot +++ b/build_files/build_environment/dependencies.dot @@ -37,10 +37,6 @@ graph[autosize = false, size = "25.7,8.3!", resolution = 300, overlap = false, s external_openimageio -- external_webp; external_openimageio -- external_opencolorio_extra; external_openmp -- external_clang; - external_opensubdiv -- external_glew; - external_opensubdiv -- external_glfw; - external_opensubdiv -- external_clew; - external_opensubdiv -- external_cuew; external_opensubdiv -- external_tbb; openvdb -- external_tbb; openvdb -- external_boost; diff --git a/build_files/build_environment/patches/cuew.diff b/build_files/build_environment/patches/cuew.diff deleted file mode 100644 index 0363034cd93..00000000000 --- a/build_files/build_environment/patches/cuew.diff +++ /dev/null @@ -1,26 +0,0 @@ ---- CmakeLists.txt.orig 2015-12-31 03:46:41 -0700 -+++ CMakeLists.txt 2016-04-01 13:28:33 -0600 -@@ -22,3 +22,10 @@ - - add_executable(testcuew cuewTest/cuewTest.c include/cuew.h) - target_link_libraries(testcuew cuew ${CMAKE_DL_LIBS}) -+ -+install(TARGETS cuew -+ LIBRARY DESTINATION lib COMPONENT libraries -+ ARCHIVE DESTINATION lib/static COMPONENT libraries) -+ -+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/cuew.h -+ DESTINATION include/) -\ No newline at end of file ---- src/cuew.c 2016-04-01 13:41:43 -0600 -+++ src/cuew.c 2016-04-01 13:41:11 -0600 -@@ -15,7 +15,9 @@ - */ - - #ifdef _MSC_VER -+#if _MSC_VER < 1900 - # define snprintf _snprintf -+#endif - # define popen _popen - # define pclose _pclose - # define _CRT_SECURE_NO_WARNINGS diff --git a/build_files/cmake/Modules/FindUSD.cmake b/build_files/cmake/Modules/FindUSD.cmake index d8f2ee22e6e..0fd5f06bb35 100644 --- a/build_files/cmake/Modules/FindUSD.cmake +++ b/build_files/cmake/Modules/FindUSD.cmake @@ -64,6 +64,7 @@ ENDIF() MARK_AS_ADVANCED( USD_INCLUDE_DIR USD_LIBRARY_DIR + USD_LIBRARY ) UNSET(_usd_SEARCH_DIRS) diff --git a/build_files/cmake/Modules/FindWebP.cmake b/build_files/cmake/Modules/FindWebP.cmake index 741c48ec447..2d8923a7fe6 100644 --- a/build_files/cmake/Modules/FindWebP.cmake +++ b/build_files/cmake/Modules/FindWebP.cmake @@ -74,4 +74,9 @@ ENDIF() MARK_AS_ADVANCED( WEBP_INCLUDE_DIR WEBP_LIBRARY_DIR + + # Generated names. + WEBP_WEBPDEMUX_LIBRARY + WEBP_WEBPMUX_LIBRARY + WEBP_WEBP_LIBRARY ) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index c5d2049b292..40c25abd585 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -104,7 +104,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099") list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi - pathcch Shcore + pathcch Shcore Dwmapi ) if(WITH_INPUT_IME) diff --git a/build_files/cmake/project_info.py b/build_files/cmake/project_info.py index 46fd538f675..0661db3a18c 100755 --- a/build_files/cmake/project_info.py +++ b/build_files/cmake/project_info.py @@ -170,7 +170,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl project_path = create_eclipse_project() if not exists(project_path): - print("Generating Eclipse Prokect File Failed: %r not found" % project_path) + print("Generating Eclipse Project File Failed: %r not found" % project_path) return None, None from xml.dom.minidom import parse diff --git a/doc/python_api/examples/bpy.types.Image.py b/doc/python_api/examples/bpy.types.Image.py new file mode 100644 index 00000000000..f0c1a780e24 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Image.py @@ -0,0 +1,46 @@ +""" +Image Data +++++++++++ + +The Image data-block is a shallow wrapper around image or video file(s) +(on disk, as packed data, or generated). + +All actual data like the pixel buffer, size, resolution etc. is +cached in an :class:`imbuf.types.ImBuf` image buffer (or several buffers +in some cases, like UDIM textures, multi-views, animations...). + +Several properties and functions of the Image data-block are then actually +using/modifying its image buffer, and not the Image data-block itself. + +.. warning:: + + One key limitation is that image buffers are not shared between different + Image data-blocks, and they are not duplicated when copying an image. + + So until a modified image buffer is saved on disk, duplicating its Image + data-block will not propagate the underlying buffer changes to the new Image. + + +This example script generates an Image data-block with a given size, +change its first pixel, rescale it, and duplicates the image. + +The duplicated image still has the same size and colors as the original image +at its creation, all editing in the original image's buffer is 'lost' in its copy. +""" + +import bpy + +image_src = bpy.data.images.new('src', 1024, 102) +print(image_src.size) +print(image_src.pixels[0:4]) + +image_src.scale(1024, 720) +image_src.pixels[0:4] = (0.5, 0.5, 0.5, 0.5) +image_src.update() +print(image_src.size) +print(image_src.pixels[0:4]) + +image_dest = image_src.copy() +image_dest.update() +print(image_dest.size) +print(image_dest.pixels[0:4]) diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py index be164a03028..5576b2d0bfe 100644 --- a/doc/python_api/examples/gpu.6.py +++ b/doc/python_api/examples/gpu.6.py @@ -29,3 +29,36 @@ def draw(): bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL') + +""" +3D Image +-------- + +Similar to the 2D Image shader, but works with 3D positions for the image vertices. +To use this example you have to provide an image that should be displayed. +""" +import bpy +import gpu +from gpu_extras.batch import batch_for_shader + +IMAGE_NAME = "Untitled" +image = bpy.data.images[IMAGE_NAME] +texture = gpu.texture.from_image(image) + +shader = gpu.shader.from_builtin('3D_IMAGE') +batch = batch_for_shader( + shader, 'TRIS', + { + "pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)), + "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)), + }, +) + + +def draw(): + shader.bind() + shader.uniform_sampler("image", texture) + batch.draw(shader) + + +bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 131b849a094..886f5345020 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1446,28 +1446,10 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel): layout.use_property_split = True world = context.world - view_layer = context.view_layer if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'): layout.prop(world, "color") - row = layout.row(align=True) - row.use_property_decorate = False - - sub = row.column(align=True) - sub.prop_search( - world, - "lightgroup", - view_layer, - "lightgroups", - text="Light Group", - results_are_suggestions=True, - ) - - sub = row.column(align=True) - sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) - sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = world.lightgroup - class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel): bl_label = "Volume" @@ -1616,6 +1598,40 @@ class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel): sub.prop(cworld, "volume_step_size") +class CYCLES_WORLD_PT_settings_light_group(CyclesButtonsPanel, Panel): + bl_label = "Light Group" + bl_parent_id = "CYCLES_WORLD_PT_settings" + bl_context = "world" + + @classmethod + def poll(cls, context): + return context.world and CyclesButtonsPanel.poll(context) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + world = context.world + view_layer = context.view_layer + + row = layout.row(align=True) + + sub = row.column(align=True) + sub.prop_search( + world, + "lightgroup", + view_layer, + "lightgroups", + text="Light Group", + results_are_suggestions=True, + ) + + sub = row.column(align=True) + sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) + sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = world.lightgroup + + class CYCLES_MATERIAL_PT_preview(CyclesButtonsPanel, Panel): bl_label = "Preview" bl_context = "material" @@ -2308,6 +2324,7 @@ classes = ( CYCLES_WORLD_PT_settings, CYCLES_WORLD_PT_settings_surface, CYCLES_WORLD_PT_settings_volume, + CYCLES_WORLD_PT_settings_light_group, CYCLES_MATERIAL_PT_preview, CYCLES_MATERIAL_PT_surface, CYCLES_MATERIAL_PT_volume, diff --git a/intern/cycles/blender/mesh.cpp b/intern/cycles/blender/mesh.cpp index 624a07762f2..c76ce3801d4 100644 --- a/intern/cycles/blender/mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -301,11 +301,11 @@ static void attr_create_sculpt_vertex_color(Scene *scene, template<typename TypeInCycles, typename GetValueAtIndex> static void fill_generic_attribute(BL::Mesh &b_mesh, TypeInCycles *data, - const AttributeElement element, + const BL::Attribute::domain_enum b_domain, const GetValueAtIndex &get_value_at_index) { - switch (element) { - case ATTR_ELEMENT_CORNER: { + switch (b_domain) { + case BL::Attribute::domain_CORNER: { for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { const int index = t.index() * 3; BL::Array<int, 3> loops = t.loops(); @@ -315,14 +315,37 @@ static void fill_generic_attribute(BL::Mesh &b_mesh, } break; } - case ATTR_ELEMENT_VERTEX: { + case BL::Attribute::domain_EDGE: { + /* Average edge attributes at vertices. */ + const size_t num_verts = b_mesh.vertices.length(); + vector<int> count(num_verts, 0); + + for (BL::MeshEdge &e : b_mesh.edges) { + BL::Array<int, 2> vertices = e.vertices(); + TypeInCycles value = get_value_at_index(e.index()); + + data[vertices[0]] += value; + data[vertices[1]] += value; + count[vertices[0]]++; + count[vertices[1]]++; + } + + for (size_t i = 0; i < num_verts; i++) { + if (count[i] > 1) { + data[i] /= (float)count[i]; + } + } + + break; + } + case BL::Attribute::domain_POINT: { const int num_verts = b_mesh.vertices.length(); for (int i = 0; i < num_verts; i++) { data[i] = get_value_at_index(i); } break; } - case ATTR_ELEMENT_FACE: { + case BL::Attribute::domain_FACE: { for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { data[t.index()] = get_value_at_index(t.polygon_index()); } @@ -404,6 +427,9 @@ static void attr_create_generic(Scene *scene, case BL::Attribute::domain_POINT: element = ATTR_ELEMENT_VERTEX; break; + case BL::Attribute::domain_EDGE: + element = ATTR_ELEMENT_VERTEX; + break; case BL::Attribute::domain_FACE: element = ATTR_ELEMENT_FACE; break; @@ -420,15 +446,16 @@ static void attr_create_generic(Scene *scene, Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); fill_generic_attribute( - b_mesh, data, element, [&](int i) { return b_float_attribute.data[i].value(); }); + b_mesh, data, b_domain, [&](int i) { return b_float_attribute.data[i].value(); }); break; } case BL::Attribute::data_type_BOOLEAN: { BL::BoolAttribute b_bool_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); - fill_generic_attribute( - b_mesh, data, element, [&](int i) { return (float)b_bool_attribute.data[i].value(); }); + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { + return (float)b_bool_attribute.data[i].value(); + }); break; } case BL::Attribute::data_type_INT: { @@ -436,14 +463,14 @@ static void attr_create_generic(Scene *scene, Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); fill_generic_attribute( - b_mesh, data, element, [&](int i) { return (float)b_int_attribute.data[i].value(); }); + b_mesh, data, b_domain, [&](int i) { return (float)b_int_attribute.data[i].value(); }); break; } case BL::Attribute::data_type_FLOAT_VECTOR: { BL::FloatVectorAttribute b_vector_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeVector, element); float3 *data = attr->data_float3(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 3> v = b_vector_attribute.data[i].vector(); return make_float3(v[0], v[1], v[2]); }); @@ -453,7 +480,7 @@ static void attr_create_generic(Scene *scene, BL::FloatColorAttribute b_color_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeRGBA, element); float4 *data = attr->data_float4(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 4> v = b_color_attribute.data[i].color(); return make_float4(v[0], v[1], v[2], v[3]); }); @@ -463,7 +490,7 @@ static void attr_create_generic(Scene *scene, BL::Float2Attribute b_float2_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeFloat2, element); float2 *data = attr->data_float2(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 2> v = b_float2_attribute.data[i].vector(); return make_float2(v[0], v[1]); }); diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index d3527567b96..35c98a71558 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -355,6 +355,18 @@ static ShaderNode *add_node(Scene *scene, else if (b_node.is_a(&RNA_ShaderNodeCombineHSV)) { node = graph->create_node<CombineHSVNode>(); } + else if (b_node.is_a(&RNA_ShaderNodeSeparateColor)) { + BL::ShaderNodeSeparateColor b_separate_node(b_node); + SeparateColorNode *separate_node = graph->create_node<SeparateColorNode>(); + separate_node->set_color_type((NodeCombSepColorType)b_separate_node.mode()); + node = separate_node; + } + else if (b_node.is_a(&RNA_ShaderNodeCombineColor)) { + BL::ShaderNodeCombineColor b_combine_node(b_node); + CombineColorNode *combine_node = graph->create_node<CombineColorNode>(); + combine_node->set_color_type((NodeCombSepColorType)b_combine_node.mode()); + node = combine_node; + } else if (b_node.is_a(&RNA_ShaderNodeSeparateXYZ)) { node = graph->create_node<SeparateXYZNode>(); } diff --git a/intern/cycles/blender/util.h b/intern/cycles/blender/util.h index eead6cec08c..49cecb6d0f3 100644 --- a/intern/cycles/blender/util.h +++ b/intern/cycles/blender/util.h @@ -262,8 +262,11 @@ static inline bool BKE_object_is_modified(BL::Object &self, BL::Scene &scene, bo static inline bool BKE_object_is_deform_modified(BObjectInfo &self, BL::Scene &scene, bool preview) { if (!self.is_real_object_data()) { - return false; + /* Comes from geometry nodes, can't use heuristic to guess if it's animated. */ + return true; } + + /* Use heuristic to quickly check if object is potentially animated. */ return self.real_object.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false; } diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 8830d8c44ac..9fc265bc327 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -23,6 +23,7 @@ # include "util/md5.h" # include "util/path.h" # include "util/progress.h" +# include "util/task.h" # include "util/time.h" # undef __KERNEL_CPU__ @@ -216,6 +217,25 @@ static OptixResult optixUtilDenoiserInvokeTiled(OptixDenoiser denoiser, return OPTIX_SUCCESS; } +# if OPTIX_ABI_VERSION >= 55 +static void execute_optix_task(TaskPool &pool, OptixTask task, OptixResult &failure_reason) +{ + OptixTask additional_tasks[16]; + unsigned int num_additional_tasks = 0; + + const OptixResult result = optixTaskExecute(task, additional_tasks, 16, &num_additional_tasks); + if (result == OPTIX_SUCCESS) { + for (unsigned int i = 0; i < num_additional_tasks; ++i) { + pool.push(function_bind( + &execute_optix_task, std::ref(pool), additional_tasks[i], std::ref(failure_reason))); + } + } + else { + failure_reason = result; + } +} +# endif + } // namespace OptiXDevice::Denoiser::Denoiser(OptiXDevice *device) @@ -453,6 +473,23 @@ bool OptiXDevice::load_kernels(const uint kernel_features) return false; } +# if OPTIX_ABI_VERSION >= 55 + OptixTask task = nullptr; + OptixResult result = optixModuleCreateFromPTXWithTasks(context, + &module_options, + &pipeline_options, + ptx_data.data(), + ptx_data.size(), + nullptr, + nullptr, + &optix_module, + &task); + if (result == OPTIX_SUCCESS) { + TaskPool pool; + execute_optix_task(pool, task, result); + pool.wait_work(); + } +# else const OptixResult result = optixModuleCreateFromPTX(context, &module_options, &pipeline_options, @@ -461,6 +498,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features) nullptr, 0, &optix_module); +# endif if (result != OPTIX_SUCCESS) { set_error(string_printf("Failed to load OptiX kernel from '%s' (%s)", ptx_filename.c_str(), diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index a75662c90d8..ebc3170393f 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -750,6 +750,11 @@ double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples int RenderScheduler::calculate_num_samples_per_update() const { const double time_per_sample_average = path_trace_time_.get_average(); + /* Fall back to 1 sample if we have not recorded a time yet. */ + if (time_per_sample_average == 0.0) { + return 1; + } + const double num_samples_in_second = pixel_size_ * pixel_size_ / time_per_sample_average; const double update_interval_in_seconds = guess_display_update_interval_in_seconds(); diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index e33330af029..473bdb67920 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -145,6 +145,7 @@ set(SRC_KERNEL_SVM_HEADERS svm/normal.h svm/ramp.h svm/ramp_util.h + svm/sepcomb_color.h svm/sepcomb_hsv.h svm/sepcomb_vector.h svm/sky.h @@ -356,16 +357,13 @@ if(WITH_CYCLES_CUDA_BINARIES) set(CUDA_VERSION "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}") # warn for other versions - if((CUDA_VERSION MATCHES "101") OR - (CUDA_VERSION MATCHES "102") OR - (CUDA_VERSION MATCHES "111") OR - (CUDA_VERSION MATCHES "112") OR - (CUDA_VERSION MATCHES "113") OR - (CUDA_VERSION MATCHES "114")) + if((CUDA_VERSION STREQUAL "101") OR + (CUDA_VERSION STREQUAL "102") OR + (CUDA_VERSION_MAJOR STREQUAL "11")) else() message(WARNING "CUDA version ${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR} detected, " - "build may succeed but only CUDA 10.1 to 11.4 are officially supported") + "build may succeed but only CUDA 11, 10.2 and 10.1 have been tested") endif() # build for each arch diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 455d15b28c2..7820d71f15c 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -1026,7 +1026,9 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, if (bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID || - bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID) { + bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID || + bsdf->type == CLOSURE_BSDF_REFRACTION_ID || + bsdf->type == CLOSURE_BSDF_SHARP_GLASS_ID) { /* Note that CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID and * CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID are treated as * CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID further below. */ @@ -1038,11 +1040,14 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, const float eta = (sd_mnee->flag & SD_BACKFACING) ? 1.0f / microfacet_bsdf->ior : microfacet_bsdf->ior; - /* Sample transmissive microfacet bsdf. */ - float bsdf_u, bsdf_v; - path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - float2 h = mnee_sample_bsdf_dh( - bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); + float2 h = zero_float2(); + if (microfacet_bsdf->alpha_x > 0.f && microfacet_bsdf->alpha_y > 0.f) { + /* Sample transmissive microfacet bsdf. */ + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + h = mnee_sample_bsdf_dh( + bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); + } /* Setup differential geometry on vertex. */ mnee_setup_manifold_vertex( diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 1df84ba765e..859c314b088 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -387,6 +387,22 @@ ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState } #endif +ccl_device_forceinline bool integrate_surface_terminate(IntegratorState state, + const uint32_t path_flag) +{ + const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? + 0.0f : + INTEGRATOR_STATE(state, path, continuation_probability); + if (probability == 0.0f) { + return true; + } + else if (probability != 1.0f) { + INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability; + } + + return false; +} + #if defined(__AO__) ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, IntegratorState state, @@ -478,12 +494,12 @@ ccl_device bool integrate_surface(KernelGlobals kg, int continue_path_label = 0; + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + /* Skip most work for volume bounding surface. */ #ifdef __VOLUME__ if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { #endif - const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); - #ifdef __SUBSURFACE__ /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP))) @@ -509,55 +525,50 @@ ccl_device bool integrate_surface(KernelGlobals kg, subsurface_shader_data_setup(kg, state, &sd, path_flag); INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SUBSURFACE; } + else #endif - - shader_prepare_surface_closures(kg, state, &sd, path_flag); + { + /* Filter closures. */ + shader_prepare_surface_closures(kg, state, &sd, path_flag); #ifdef __HOLDOUT__ - /* Evaluate holdout. */ - if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) { - return false; - } + /* Evaluate holdout. */ + if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) { + return false; + } #endif #ifdef __EMISSION__ - /* Write emission. */ - if (sd.flag & SD_EMISSION) { - integrate_surface_emission(kg, state, &sd, render_buffer); - } + /* Write emission. */ + if (sd.flag & SD_EMISSION) { + integrate_surface_emission(kg, state, &sd, render_buffer); + } #endif + /* Perform path termination. Most paths have already been terminated in + * the intersect_closest kernel, this is just for emission and for dividing + * throughput by the probability at the right moment. + * + * Also ensure we don't do it twice for SSS at both the entry and exit point. */ + if (integrate_surface_terminate(state, path_flag)) { + return false; + } + + /* Write render passes. */ #ifdef __PASSES__ - /* Write render passes. */ - PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES); - kernel_write_data_passes(kg, state, &sd, render_buffer); + PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES); + kernel_write_data_passes(kg, state, &sd, render_buffer); #endif +#ifdef __DENOISING_FEATURES__ + kernel_write_denoising_features_surface(kg, state, &sd, render_buffer); +#endif + } + /* Load random number state. */ RNGState rng_state; path_state_rng_load(state, &rng_state); - /* Perform path termination. Most paths have already been terminated in - * the intersect_closest kernel, this is just for emission and for dividing - * throughput by the probability at the right moment. - * - * Also ensure we don't do it twice for SSS at both the entry and exit point. */ - if (!(path_flag & PATH_RAY_SUBSURFACE)) { - const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? - 0.0f : - INTEGRATOR_STATE(state, path, continuation_probability); - if (probability == 0.0f) { - return false; - } - else if (probability != 1.0f) { - INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability; - } - } - -#ifdef __DENOISING_FEATURES__ - kernel_write_denoising_features_surface(kg, state, &sd, render_buffer); -#endif - /* Direct light. */ PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT); integrate_surface_direct_light<node_feature_mask>(kg, state, &sd, &rng_state); @@ -575,6 +586,10 @@ ccl_device bool integrate_surface(KernelGlobals kg, #ifdef __VOLUME__ } else { + if (integrate_surface_terminate(state, path_flag)) { + return false; + } + PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); continue_path_label = integrate_surface_volume_only_bounce(state, &sd); } diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 5acfc92cca1..9bbbd5b0d10 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -143,7 +143,7 @@ ccl_device_inline float3 shadow_ray_smooth_surface_offset( float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ + object_dir_transform(kg, sd, &n); /* Normal x scale, to world space */ } /* Parabolic approximation */ diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt index 7ced21c5670..741bce7c399 100644 --- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -16,6 +16,7 @@ set(SRC_OSL node_camera.osl node_checker_texture.osl node_clamp.osl + node_combine_color.osl node_combine_rgb.osl node_combine_hsv.osl node_combine_xyz.osl @@ -68,6 +69,7 @@ set(SRC_OSL node_refraction_bsdf.osl node_rgb_curves.osl node_rgb_ramp.osl + node_separate_color.osl node_separate_rgb.osl node_separate_hsv.osl node_separate_xyz.osl diff --git a/intern/cycles/kernel/osl/shaders/node_color.h b/intern/cycles/kernel/osl/shaders/node_color.h index 388dd114e9a..06735f5b03d 100644 --- a/intern/cycles/kernel/osl/shaders/node_color.h +++ b/intern/cycles/kernel/osl/shaders/node_color.h @@ -148,3 +148,53 @@ color hsv_to_rgb(color hsv) return rgb; } + +color rgb_to_hsl(color rgb) +{ + float cmax, cmin, h, s, l; + + cmax = max(rgb[0], max(rgb[1], rgb[2])); + cmin = min(rgb[0], min(rgb[1], rgb[2])); + l = min(1.0, (cmax + cmin) / 2.0); + + if (cmax == cmin) { + h = s = 0.0; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb[0]) { + h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0); + } + else if (cmax == rgb[1]) { + h = (rgb[2] - rgb[0]) / cdelta + 2.0; + } + else { + h = (rgb[0] - rgb[1]) / cdelta + 4.0; + } + } + h /= 6.0; + + return color(h, s, l); +} + +color hsl_to_rgb(color hsl) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl[0]; + s = hsl[1]; + l = hsl[2]; + + nr = abs(h * 6.0 - 3.0) - 1.0; + ng = 2.0 - abs(h * 6.0 - 2.0); + nb = 2.0 - abs(h * 6.0 - 4.0); + + nr = clamp(nr, 0.0, 1.0); + nb = clamp(nb, 0.0, 1.0); + ng = clamp(ng, 0.0, 1.0); + + chroma = (1.0 - abs(2.0 * l - 1.0)) * s; + + return color((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l); +} diff --git a/intern/cycles/kernel/osl/shaders/node_combine_color.osl b/intern/cycles/kernel/osl/shaders/node_combine_color.osl new file mode 100644 index 00000000000..681a592d2bb --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_combine_color.osl @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include "stdcycles.h" + +shader node_combine_color(string color_type = "rgb", + float Red = 0.0, + float Green = 0.0, + float Blue = 0.0, + output color Color = 0.8) +{ + if (color_type == "rgb" || color_type == "hsv" || color_type == "hsl") + Color = color(color_type, Red, Green, Blue); + else + warning("%s", "Unknown color space!"); +} diff --git a/intern/cycles/kernel/osl/shaders/node_separate_color.osl b/intern/cycles/kernel/osl/shaders/node_separate_color.osl new file mode 100644 index 00000000000..6f3e3149d8e --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_separate_color.osl @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include "node_color.h" +#include "stdcycles.h" + +shader node_separate_color(string color_type = "rgb", + color Color = 0.8, + output float Red = 0.0, + output float Green = 0.0, + output float Blue = 0.0) +{ + color col; + if (color_type == "rgb") + col = Color; + else if (color_type == "hsv") + col = rgb_to_hsv(Color); + else if (color_type == "hsl") + col = rgb_to_hsl(Color); + else + warning("%s", "Unknown color space!"); + + Red = col[0]; + Green = col[1]; + Blue = col[2]; +} diff --git a/intern/cycles/kernel/svm/color_util.h b/intern/cycles/kernel/svm/color_util.h index b439721383c..fa22d4bc8c2 100644 --- a/intern/cycles/kernel/svm/color_util.h +++ b/intern/cycles/kernel/svm/color_util.h @@ -307,4 +307,30 @@ ccl_device_inline float3 svm_brightness_contrast(float3 color, float brightness, return color; } +ccl_device float3 svm_combine_color(NodeCombSepColorType type, float3 color) +{ + switch (type) { + case NODE_COMBSEP_COLOR_HSV: + return hsv_to_rgb(color); + case NODE_COMBSEP_COLOR_HSL: + return hsl_to_rgb(color); + case NODE_COMBSEP_COLOR_RGB: + default: + return color; + } +} + +ccl_device float3 svm_separate_color(NodeCombSepColorType type, float3 color) +{ + switch (type) { + case NODE_COMBSEP_COLOR_HSV: + return rgb_to_hsv(color); + case NODE_COMBSEP_COLOR_HSL: + return rgb_to_hsl(color); + case NODE_COMBSEP_COLOR_RGB: + default: + return color; + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/displace.h b/intern/cycles/kernel/svm/displace.h index eb74e23b1a4..56fb5c3f9b0 100644 --- a/intern/cycles/kernel/svm/displace.h +++ b/intern/cycles/kernel/svm/displace.h @@ -8,159 +8,188 @@ CCL_NAMESPACE_BEGIN /* Bump Node */ - +template<uint node_feature_mask> ccl_device_noinline void svm_node_set_bump(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) { #ifdef __RAY_DIFFERENTIALS__ - /* get normal input */ - uint normal_offset, scale_offset, invert, use_object_space; - svm_unpack_node_uchar4(node.y, &normal_offset, &scale_offset, &invert, &use_object_space); - - float3 normal_in = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; + IF_KERNEL_NODES_FEATURE(BUMP) + { + /* get normal input */ + uint normal_offset, scale_offset, invert, use_object_space; + svm_unpack_node_uchar4(node.y, &normal_offset, &scale_offset, &invert, &use_object_space); + + float3 normal_in = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : + sd->N; + + float3 dPdx = sd->dP.dx; + float3 dPdy = sd->dP.dy; + + if (use_object_space) { + object_inverse_normal_transform(kg, sd, &normal_in); + object_inverse_dir_transform(kg, sd, &dPdx); + object_inverse_dir_transform(kg, sd, &dPdy); + } - float3 dPdx = sd->dP.dx; - float3 dPdy = sd->dP.dy; + /* get surface tangents from normal */ + float3 Rx = cross(dPdy, normal_in); + float3 Ry = cross(normal_in, dPdx); - if (use_object_space) { - object_inverse_normal_transform(kg, sd, &normal_in); - object_inverse_dir_transform(kg, sd, &dPdx); - object_inverse_dir_transform(kg, sd, &dPdy); - } + /* get bump values */ + uint c_offset, x_offset, y_offset, strength_offset; + svm_unpack_node_uchar4(node.z, &c_offset, &x_offset, &y_offset, &strength_offset); - /* get surface tangents from normal */ - float3 Rx = cross(dPdy, normal_in); - float3 Ry = cross(normal_in, dPdx); + float h_c = stack_load_float(stack, c_offset); + float h_x = stack_load_float(stack, x_offset); + float h_y = stack_load_float(stack, y_offset); - /* get bump values */ - uint c_offset, x_offset, y_offset, strength_offset; - svm_unpack_node_uchar4(node.z, &c_offset, &x_offset, &y_offset, &strength_offset); + /* compute surface gradient and determinant */ + float det = dot(dPdx, Rx); + float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry; - float h_c = stack_load_float(stack, c_offset); - float h_x = stack_load_float(stack, x_offset); - float h_y = stack_load_float(stack, y_offset); + float absdet = fabsf(det); - /* compute surface gradient and determinant */ - float det = dot(dPdx, Rx); - float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry; + float strength = stack_load_float(stack, strength_offset); + float scale = stack_load_float(stack, scale_offset); - float absdet = fabsf(det); + if (invert) + scale *= -1.0f; - float strength = stack_load_float(stack, strength_offset); - float scale = stack_load_float(stack, scale_offset); + strength = max(strength, 0.0f); - if (invert) - scale *= -1.0f; + /* compute and output perturbed normal */ + float3 normal_out = safe_normalize(absdet * normal_in - scale * signf(det) * surfgrad); + if (is_zero(normal_out)) { + normal_out = normal_in; + } + else { + normal_out = normalize(strength * normal_out + (1.0f - strength) * normal_in); + } - strength = max(strength, 0.0f); + if (use_object_space) { + object_normal_transform(kg, sd, &normal_out); + } - /* compute and output perturbed normal */ - float3 normal_out = safe_normalize(absdet * normal_in - scale * signf(det) * surfgrad); - if (is_zero(normal_out)) { - normal_out = normal_in; - } - else { - normal_out = normalize(strength * normal_out + (1.0f - strength) * normal_in); + normal_out = ensure_valid_reflection(sd->Ng, sd->I, normal_out); + stack_store_float3(stack, node.w, normal_out); } - - if (use_object_space) { - object_normal_transform(kg, sd, &normal_out); + else + { + stack_store_float3(stack, node.w, zero_float3()); } - - normal_out = ensure_valid_reflection(sd->Ng, sd->I, normal_out); - - stack_store_float3(stack, node.w, normal_out); #endif } /* Displacement Node */ +template<uint node_feature_mask> ccl_device void svm_node_set_displacement(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint fac_offset) { - float3 dP = stack_load_float3(stack, fac_offset); - sd->P += dP; + IF_KERNEL_NODES_FEATURE(BUMP) + { + float3 dP = stack_load_float3(stack, fac_offset); + sd->P += dP; + } } +template<uint node_feature_mask> ccl_device_noinline void svm_node_displacement(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node) { - uint height_offset, midlevel_offset, scale_offset, normal_offset; - svm_unpack_node_uchar4(node.y, &height_offset, &midlevel_offset, &scale_offset, &normal_offset); - - float height = stack_load_float(stack, height_offset); - float midlevel = stack_load_float(stack, midlevel_offset); - float scale = stack_load_float(stack, scale_offset); - float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; - uint space = node.w; - - float3 dP = normal; - - if (space == NODE_NORMAL_MAP_OBJECT) { - /* Object space. */ - object_inverse_normal_transform(kg, sd, &dP); - dP *= (height - midlevel) * scale; - object_dir_transform(kg, sd, &dP); + IF_KERNEL_NODES_FEATURE(BUMP) + { + uint height_offset, midlevel_offset, scale_offset, normal_offset; + svm_unpack_node_uchar4( + node.y, &height_offset, &midlevel_offset, &scale_offset, &normal_offset); + + float height = stack_load_float(stack, height_offset); + float midlevel = stack_load_float(stack, midlevel_offset); + float scale = stack_load_float(stack, scale_offset); + float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; + uint space = node.w; + + float3 dP = normal; + + if (space == NODE_NORMAL_MAP_OBJECT) { + /* Object space. */ + object_inverse_normal_transform(kg, sd, &dP); + dP *= (height - midlevel) * scale; + object_dir_transform(kg, sd, &dP); + } + else { + /* World space. */ + dP *= (height - midlevel) * scale; + } + + stack_store_float3(stack, node.z, dP); } - else { - /* World space. */ - dP *= (height - midlevel) * scale; + else + { + stack_store_float3(stack, node.z, zero_float3()); } - - stack_store_float3(stack, node.z, dP); } +template<uint node_feature_mask> ccl_device_noinline int svm_node_vector_displacement( KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint4 data_node = read_node(kg, &offset); - uint space = data_node.x; - uint vector_offset, midlevel_offset, scale_offset, displacement_offset; svm_unpack_node_uchar4( node.y, &vector_offset, &midlevel_offset, &scale_offset, &displacement_offset); - float3 vector = stack_load_float3(stack, vector_offset); - float midlevel = stack_load_float(stack, midlevel_offset); - float scale = stack_load_float(stack, scale_offset); - float3 dP = (vector - make_float3(midlevel, midlevel, midlevel)) * scale; - - if (space == NODE_NORMAL_MAP_TANGENT) { - /* Tangent space. */ - float3 normal = sd->N; - object_inverse_normal_transform(kg, sd, &normal); - - const AttributeDescriptor attr = find_attribute(kg, sd, node.z); - float3 tangent; - if (attr.offset != ATTR_STD_NOT_FOUND) { - tangent = primitive_surface_attribute_float3(kg, sd, attr, NULL, NULL); - } - else { - tangent = normalize(sd->dPdu); + IF_KERNEL_NODES_FEATURE(BUMP) + { + uint space = data_node.x; + + float3 vector = stack_load_float3(stack, vector_offset); + float midlevel = stack_load_float(stack, midlevel_offset); + float scale = stack_load_float(stack, scale_offset); + float3 dP = (vector - make_float3(midlevel, midlevel, midlevel)) * scale; + + if (space == NODE_NORMAL_MAP_TANGENT) { + /* Tangent space. */ + float3 normal = sd->N; + object_inverse_normal_transform(kg, sd, &normal); + + const AttributeDescriptor attr = find_attribute(kg, sd, node.z); + float3 tangent; + if (attr.offset != ATTR_STD_NOT_FOUND) { + tangent = primitive_surface_attribute_float3(kg, sd, attr, NULL, NULL); + } + else { + tangent = normalize(sd->dPdu); + } + + float3 bitangent = normalize(cross(normal, tangent)); + const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w); + if (attr_sign.offset != ATTR_STD_NOT_FOUND) { + float sign = primitive_surface_attribute_float(kg, sd, attr_sign, NULL, NULL); + bitangent *= sign; + } + + dP = tangent * dP.x + normal * dP.y + bitangent * dP.z; } - float3 bitangent = normalize(cross(normal, tangent)); - const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w); - if (attr_sign.offset != ATTR_STD_NOT_FOUND) { - float sign = primitive_surface_attribute_float(kg, sd, attr_sign, NULL, NULL); - bitangent *= sign; + if (space != NODE_NORMAL_MAP_WORLD) { + /* Tangent or object space. */ + object_dir_transform(kg, sd, &dP); } - dP = tangent * dP.x + normal * dP.y + bitangent * dP.z; + stack_store_float3(stack, displacement_offset, dP); } - - if (space != NODE_NORMAL_MAP_WORLD) { - /* Tangent or object space. */ - object_dir_transform(kg, sd, &dP); + else + { + stack_store_float3(stack, displacement_offset, zero_float3()); } - stack_store_float3(stack, displacement_offset, dP); return offset; } diff --git a/intern/cycles/kernel/svm/sepcomb_color.h b/intern/cycles/kernel/svm/sepcomb_color.h new file mode 100644 index 00000000000..d186e7f163b --- /dev/null +++ b/intern/cycles/kernel/svm/sepcomb_color.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +ccl_device_noinline void svm_node_combine_color(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint color_type, + uint inputs_stack_offsets, + uint result_stack_offset) +{ + uint red_stack_offset, green_stack_offset, blue_stack_offset; + svm_unpack_node_uchar3( + inputs_stack_offsets, &red_stack_offset, &green_stack_offset, &blue_stack_offset); + + float r = stack_load_float(stack, red_stack_offset); + float g = stack_load_float(stack, green_stack_offset); + float b = stack_load_float(stack, blue_stack_offset); + + /* Combine, and convert back to RGB */ + float3 color = svm_combine_color((NodeCombSepColorType)color_type, make_float3(r, g, b)); + + if (stack_valid(result_stack_offset)) + stack_store_float3(stack, result_stack_offset, color); +} + +ccl_device_noinline void svm_node_separate_color(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint color_type, + uint input_stack_offset, + uint results_stack_offsets) +{ + float3 color = stack_load_float3(stack, input_stack_offset); + + /* Convert color space */ + color = svm_separate_color((NodeCombSepColorType)color_type, color); + + uint red_stack_offset, green_stack_offset, blue_stack_offset; + svm_unpack_node_uchar3( + results_stack_offsets, &red_stack_offset, &green_stack_offset, &blue_stack_offset); + + if (stack_valid(red_stack_offset)) + stack_store_float(stack, red_stack_offset, color.x); + if (stack_valid(green_stack_offset)) + stack_store_float(stack, green_stack_offset, color.y); + if (stack_valid(blue_stack_offset)) + stack_store_float(stack, blue_stack_offset, color.z); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 6b969da7e52..5def943c87f 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -181,6 +181,7 @@ CCL_NAMESPACE_END #include "kernel/svm/noisetex.h" #include "kernel/svm/normal.h" #include "kernel/svm/ramp.h" +#include "kernel/svm/sepcomb_color.h" #include "kernel/svm/sepcomb_hsv.h" #include "kernel/svm/sepcomb_vector.h" #include "kernel/svm/sky.h" @@ -304,22 +305,13 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, } break; case NODE_SET_DISPLACEMENT: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_set_displacement(kg, sd, stack, node.y); - } + svm_node_set_displacement<node_feature_mask>(kg, sd, stack, node.y); break; case NODE_DISPLACEMENT: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_displacement(kg, sd, stack, node); - } + svm_node_displacement<node_feature_mask>(kg, sd, stack, node); break; case NODE_VECTOR_DISPLACEMENT: - IF_KERNEL_NODES_FEATURE(BUMP) - { - offset = svm_node_vector_displacement(kg, sd, stack, node, offset); - } + offset = svm_node_vector_displacement<node_feature_mask>(kg, sd, stack, node, offset); break; case NODE_TEX_IMAGE: offset = svm_node_tex_image(kg, sd, stack, node, offset); @@ -331,10 +323,7 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_SET_BUMP: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_set_bump(kg, sd, stack, node); - } + svm_node_set_bump<node_feature_mask>(kg, sd, stack, node); break; case NODE_ATTR_BUMP_DX: IF_KERNEL_NODES_FEATURE(BUMP) @@ -520,6 +509,12 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, case NODE_MIX: offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); break; + case NODE_SEPARATE_COLOR: + svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w); + break; + case NODE_COMBINE_COLOR: + svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w); + break; case NODE_SEPARATE_VECTOR: svm_node_separate_vector(sd, stack, node.y, node.z, node.w); break; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index bede58f7a54..82109ec4c4f 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -92,6 +92,8 @@ typedef enum ShaderNodeType { NODE_NORMAL_MAP, NODE_INVERT, NODE_MIX, + NODE_SEPARATE_COLOR, + NODE_COMBINE_COLOR, NODE_SEPARATE_VECTOR, NODE_COMBINE_VECTOR, NODE_SEPARATE_HSV, @@ -487,6 +489,12 @@ typedef enum NodePrincipledHairParametrization { NODE_PRINCIPLED_HAIR_NUM, } NodePrincipledHairParametrization; +typedef enum NodeCombSepColorType { + NODE_COMBSEP_COLOR_RGB, + NODE_COMBSEP_COLOR_HSV, + NODE_COMBSEP_COLOR_HSL, +} NodeCombSepColorType; + /* Closure */ typedef enum ClosureType { diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index 349d8ad39c7..9152abacbdb 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -618,6 +618,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, for (size_t k = 0; k < size; k++) { attr_uchar4[offset + k] = data[k]; } + attr_uchar4.tag_modified(); } attr_uchar4_offset += size; } @@ -630,6 +631,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, for (size_t k = 0; k < size; k++) { attr_float[offset + k] = data[k]; } + attr_float.tag_modified(); } attr_float_offset += size; } @@ -642,6 +644,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, for (size_t k = 0; k < size; k++) { attr_float2[offset + k] = data[k]; } + attr_float2.tag_modified(); } attr_float2_offset += size; } @@ -654,6 +657,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, for (size_t k = 0; k < size * 3; k++) { attr_float4[offset + k] = (&tfm->x)[k]; } + attr_float4.tag_modified(); } attr_float4_offset += size * 3; } @@ -666,6 +670,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, for (size_t k = 0; k < size; k++) { attr_float4[offset + k] = data[k]; } + attr_float4.tag_modified(); } attr_float4_offset += size; } @@ -678,6 +683,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, for (size_t k = 0; k < size; k++) { attr_float3[offset + k] = data[k]; } + attr_float3.tag_modified(); } attr_float3_offset += size; } diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 95fccf725f3..9a61a8a753b 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -19,7 +19,6 @@ #include "util/color.h" #include "util/foreach.h" #include "util/log.h" -#include "util/string.h" #include "util/transform.h" #include "kernel/tables.h" @@ -450,12 +449,8 @@ void ImageTextureNode::compile(OSLCompiler &compiler) const ustring known_colorspace = metadata.colorspace; if (handle.svm_slot() == -1) { - /* OIIO currently does not support <UVTILE> substitutions natively. Replace with a format they - * understand. */ - std::string osl_filename = filename.string(); - string_replace(osl_filename, "<UVTILE>", "<U>_<V>"); compiler.parameter_texture( - "filename", ustring(osl_filename), compress_as_srgb ? u_colorspace_raw : known_colorspace); + "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { compiler.parameter_texture("filename", handle.svm_slot()); @@ -5010,6 +5005,63 @@ void MixNode::constant_fold(const ConstantFolder &folder) } } +/* Combine Color */ + +NODE_DEFINE(CombineColorNode) +{ + NodeType *type = NodeType::add("combine_color", create, NodeType::SHADER); + + static NodeEnum type_enum; + type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB); + type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV); + type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL); + SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB); + + SOCKET_IN_FLOAT(r, "Red", 0.0f); + SOCKET_IN_FLOAT(g, "Green", 0.0f); + SOCKET_IN_FLOAT(b, "Blue", 0.0f); + + SOCKET_OUT_COLOR(color, "Color"); + + return type; +} + +CombineColorNode::CombineColorNode() : ShaderNode(get_node_type()) +{ +} + +void CombineColorNode::constant_fold(const ConstantFolder &folder) +{ + if (folder.all_inputs_constant()) { + folder.make_constant(svm_combine_color(color_type, make_float3(r, g, b))); + } +} + +void CombineColorNode::compile(SVMCompiler &compiler) +{ + ShaderInput *red_in = input("Red"); + ShaderInput *green_in = input("Green"); + ShaderInput *blue_in = input("Blue"); + ShaderOutput *color_out = output("Color"); + + int red_stack_offset = compiler.stack_assign(red_in); + int green_stack_offset = compiler.stack_assign(green_in); + int blue_stack_offset = compiler.stack_assign(blue_in); + int color_stack_offset = compiler.stack_assign(color_out); + + compiler.add_node( + NODE_COMBINE_COLOR, + color_type, + compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset), + color_stack_offset); +} + +void CombineColorNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "color_type"); + compiler.add(this, "node_combine_color"); +} + /* Combine RGB */ NODE_DEFINE(CombineRGBNode) @@ -5250,6 +5302,70 @@ void BrightContrastNode::compile(OSLCompiler &compiler) compiler.add(this, "node_brightness"); } +/* Separate Color */ + +NODE_DEFINE(SeparateColorNode) +{ + NodeType *type = NodeType::add("separate_color", create, NodeType::SHADER); + + static NodeEnum type_enum; + type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB); + type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV); + type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL); + SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB); + + SOCKET_IN_COLOR(color, "Color", zero_float3()); + + SOCKET_OUT_FLOAT(r, "Red"); + SOCKET_OUT_FLOAT(g, "Green"); + SOCKET_OUT_FLOAT(b, "Blue"); + + return type; +} + +SeparateColorNode::SeparateColorNode() : ShaderNode(get_node_type()) +{ +} + +void SeparateColorNode::constant_fold(const ConstantFolder &folder) +{ + if (folder.all_inputs_constant()) { + float3 col = svm_separate_color(color_type, color); + + for (int channel = 0; channel < 3; channel++) { + if (outputs[channel] == folder.output) { + folder.make_constant(col[channel]); + return; + } + } + } +} + +void SeparateColorNode::compile(SVMCompiler &compiler) +{ + ShaderInput *color_in = input("Color"); + ShaderOutput *red_out = output("Red"); + ShaderOutput *green_out = output("Green"); + ShaderOutput *blue_out = output("Blue"); + + int color_stack_offset = compiler.stack_assign(color_in); + int red_stack_offset = compiler.stack_assign(red_out); + int green_stack_offset = compiler.stack_assign(green_out); + int blue_stack_offset = compiler.stack_assign(blue_out); + + compiler.add_node( + NODE_SEPARATE_COLOR, + color_type, + color_stack_offset, + compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset)); +} + +void SeparateColorNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "color_type"); + compiler.add(this, "node_separate_color"); +} + /* Separate RGB */ NODE_DEFINE(SeparateRGBNode) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 9aef5d3151f..ac40a397c1e 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -1101,6 +1101,17 @@ class MixNode : public ShaderNode { NODE_SOCKET_API(float, fac) }; +class CombineColorNode : public ShaderNode { + public: + SHADER_NODE_CLASS(CombineColorNode) + void constant_fold(const ConstantFolder &folder); + + NODE_SOCKET_API(NodeCombSepColorType, color_type) + NODE_SOCKET_API(float, r) + NODE_SOCKET_API(float, g) + NODE_SOCKET_API(float, b) +}; + class CombineRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineRGBNode) @@ -1150,6 +1161,15 @@ class BrightContrastNode : public ShaderNode { NODE_SOCKET_API(float, contrast) }; +class SeparateColorNode : public ShaderNode { + public: + SHADER_NODE_CLASS(SeparateColorNode) + void constant_fold(const ConstantFolder &folder); + + NODE_SOCKET_API(NodeCombSepColorType, color_type) + NODE_SOCKET_API(float3, color) +}; + class SeparateRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateRGBNode) diff --git a/intern/cycles/util/color.h b/intern/cycles/util/color.h index cccccde3ba6..795c3754976 100644 --- a/intern/cycles/util/color.h +++ b/intern/cycles/util/color.h @@ -152,6 +152,56 @@ ccl_device float3 hsv_to_rgb(float3 hsv) return rgb; } +ccl_device float3 rgb_to_hsl(float3 rgb) +{ + float cmax, cmin, h, s, l; + + cmax = fmaxf(rgb.x, fmaxf(rgb.y, rgb.z)); + cmin = min(rgb.x, min(rgb.y, rgb.z)); + l = min(1.0f, (cmax + cmin) / 2.0f); + + if (cmax == cmin) { + h = s = 0.0f; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5f ? cdelta / (2.0f - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb.x) { + h = (rgb.y - rgb.z) / cdelta + (rgb.y < rgb.z ? 6.0f : 0.0f); + } + else if (cmax == rgb.y) { + h = (rgb.z - rgb.x) / cdelta + 2.0f; + } + else { + h = (rgb.x - rgb.y) / cdelta + 4.0f; + } + } + h /= 6.0f; + + return make_float3(h, s, l); +} + +ccl_device float3 hsl_to_rgb(float3 hsl) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl.x; + s = hsl.y; + l = hsl.z; + + nr = fabsf(h * 6.0f - 3.0f) - 1.0f; + ng = 2.0f - fabsf(h * 6.0f - 2.0f); + nb = 2.0f - fabsf(h * 6.0f - 4.0f); + + nr = clamp(nr, 0.0f, 1.0f); + nb = clamp(nb, 0.0f, 1.0f); + ng = clamp(ng, 0.0f, 1.0f); + + chroma = (1.0f - fabsf(2.0f * l - 1.0f)) * s; + + return make_float3((nr - 0.5f) * chroma + l, (ng - 0.5f) * chroma + l, (nb - 0.5f) * chroma + l); +} + ccl_device float3 xyY_to_xyz(float x, float y, float Y) { float X, Z; diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 9421edecf12..dceb9ced803 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -376,6 +376,7 @@ elseif(WIN32) intern/GHOST_DisplayManagerWin32.cpp intern/GHOST_DropTargetWin32.cpp intern/GHOST_SystemWin32.cpp + intern/GHOST_TrackpadWin32.cpp intern/GHOST_WindowWin32.cpp intern/GHOST_Wintab.cpp @@ -384,6 +385,7 @@ elseif(WIN32) intern/GHOST_DropTargetWin32.h intern/GHOST_SystemWin32.h intern/GHOST_TaskbarWin32.h + intern/GHOST_TrackpadWin32.h intern/GHOST_WindowWin32.h intern/GHOST_Wintab.h ) diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 83869188b65..8e07bf4ea3d 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -8,12 +8,14 @@ #include "GHOST_SystemWin32.h" #include "GHOST_ContextD3D.h" #include "GHOST_EventDragnDrop.h" +#include "GHOST_EventTrackpad.h" #ifndef _WIN32_IE # define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */ #endif #include <commctrl.h> +#include <dwmapi.h> #include <psapi.h> #include <shellapi.h> #include <shellscalingapi.h> @@ -414,6 +416,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent) hasEventHandled = true; } + driveTrackpad(); + // Process all the events waiting for us while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. @@ -423,6 +427,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent) hasEventHandled = true; } + processTrackpad(); + /* PeekMessage above is allowed to dispatch messages to the wndproc without us * noticing, so we need to check the event manager here to see if there are * events waiting in the queue. @@ -1416,6 +1422,52 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) } #endif // WITH_INPUT_NDOF +void GHOST_SystemWin32::driveTrackpad() +{ + GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>( + getWindowManager()->getActiveWindow()); + if (active_window) { + active_window->updateDirectManipulation(); + } +} + +void GHOST_SystemWin32::processTrackpad() +{ + GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>( + getWindowManager()->getActiveWindow()); + + if (!active_window) { + return; + } + + GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo(); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + int32_t cursor_x, cursor_y; + system->getCursorPosition(cursor_x, cursor_y); + + if (trackpad_info.x != 0 || trackpad_info.y != 0) { + system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(), + active_window, + GHOST_kTrackpadEventScroll, + cursor_x, + cursor_y, + trackpad_info.x, + trackpad_info.y, + trackpad_info.isScrollDirectionInverted)); + } + if (trackpad_info.scale != 0) { + system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(), + active_window, + GHOST_kTrackpadEventMagnify, + cursor_x, + cursor_y, + trackpad_info.scale, + 0, + false)); + } +} + LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { GHOST_Event *event = NULL; @@ -1968,6 +2020,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, suggestedWindowRect->right - suggestedWindowRect->left, suggestedWindowRect->bottom - suggestedWindowRect->top, SWP_NOZORDER | SWP_NOACTIVATE); + + window->updateDPI(); } break; case WM_DISPLAYCHANGE: { @@ -1985,6 +2039,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, ::SetFocus(hwnd); } break; + case WM_SETTINGCHANGE: + /* Microsoft: "Note that some applications send this message with lParam set to NULL" */ + if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) { + window->ThemeRefresh(); + } + break; //////////////////////////////////////////////////////////////////////// // Window events, ignored //////////////////////////////////////////////////////////////////////// @@ -2056,6 +2116,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * In GHOST, we let DefWindowProc call the timer callback. */ break; + case DM_POINTERHITTEST: + /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first + * detected, in order to determine the most probable input target for Direct + * Manipulation. */ + window->onPointerHitTest(wParam); + break; } } else { diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 9f8d52f9ca3..689b78b0317 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -407,6 +407,16 @@ class GHOST_SystemWin32 : public GHOST_System { #endif /** + * Drives Direct Manipulation update. + */ + void driveTrackpad(); + + /** + * Creates trackpad events for the active window. + */ + void processTrackpad(); + + /** * Returns the local state of the modifier keys (from the message queue). * \param keys: The state of the keys. */ diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.cpp b/intern/ghost/intern/GHOST_TrackpadWin32.cpp new file mode 100644 index 00000000000..d5317f0f780 --- /dev/null +++ b/intern/ghost/intern/GHOST_TrackpadWin32.cpp @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + */ + +#include <cmath> + +#include "GHOST_Debug.h" +#include "GHOST_TrackpadWin32.h" + +GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper( + HWND hWnd, + Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager, + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager, + Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport, + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + directManipulationEventHandler, + DWORD directManipulationViewportHandlerCookie, + bool isScrollDirectionInverted) + : m_hWnd(hWnd), + m_scrollDirectionRegKey(NULL), + m_scrollDirectionChangeEvent(NULL), + m_directManipulationManager(directManipulationManager), + m_directManipulationUpdateManager(directManipulationUpdateManager), + m_directManipulationViewport(directManipulationViewport), + m_directManipulationEventHandler(directManipulationEventHandler), + m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie), + m_isScrollDirectionInverted(isScrollDirectionInverted) +{ +} + +GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi) +{ +#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \ + { \ + if (!SUCCEEDED(hr)) { \ + GHOST_PRINT(failMessage); \ + return nullptr; \ + } \ + } + + Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager; + HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager, + nullptr, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&directManipulationManager)); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n"); + + /* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */ + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager; + hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager)); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n"); + + Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport; + hr = directManipulationManager->CreateViewport( + nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport)); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n"); + + DIRECTMANIPULATION_CONFIGURATION configuration = + DIRECTMANIPULATION_CONFIGURATION_INTERACTION | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA | + DIRECTMANIPULATION_CONFIGURATION_SCALING; + + hr = directManipulationViewport->ActivateConfiguration(configuration); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n"); + + /* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we + * need to use MANUALUPDATE option. */ + hr = directManipulationViewport->SetViewportOptions( + DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n"); + + /* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler + * callbacks. */ + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + directManipulationEventHandler = + Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi); + DWORD directManipulationViewportHandlerCookie; + directManipulationViewport->AddEventHandler( + hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n"); + + /* Set default rect for viewport before activating. */ + RECT rect = {0, 0, 10000, 10000}; + hr = directManipulationViewport->SetViewportRect(&rect); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n"); + + hr = directManipulationManager->Activate(hWnd); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n"); + + hr = directManipulationViewport->Enable(); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n"); + + directManipulationEventHandler->resetViewport(directManipulationViewport.Get()); + + bool isScrollDirectionInverted = getScrollDirectionFromReg(); + + auto instance = new GHOST_DirectManipulationHelper(hWnd, + directManipulationManager, + directManipulationUpdateManager, + directManipulationViewport, + directManipulationEventHandler, + directManipulationViewportHandlerCookie, + isScrollDirectionInverted); + + instance->registerScrollDirectionChangeListener(); + + return instance; + +#undef DM_CHECK_RESULT_AND_EXIT_EARLY +} + +bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg() +{ + DWORD scrollDirectionRegValue, pcbData; + HRESULT hr = HRESULT_FROM_WIN32( + RegGetValueW(HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\", + L"ScrollDirection", + RRF_RT_REG_DWORD, + NULL, + &scrollDirectionRegValue, + &pcbData)); + if (!SUCCEEDED(hr)) { + GHOST_PRINT("Failed to get scroll direction from registry\n"); + return false; + } + + return scrollDirectionRegValue == 0; +} + +void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener() +{ + + if (!m_scrollDirectionRegKey) { + HRESULT hr = HRESULT_FROM_WIN32( + RegOpenKeyExW(HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\", + 0, + KEY_NOTIFY, + &m_scrollDirectionRegKey)); + if (!SUCCEEDED(hr)) { + GHOST_PRINT("Failed to open scroll direction registry key\n"); + return; + } + } + + if (!m_scrollDirectionChangeEvent) { + m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL); + } + else { + ResetEvent(m_scrollDirectionChangeEvent); + } + HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey, + true, + REG_NOTIFY_CHANGE_LAST_SET, + m_scrollDirectionChangeEvent, + true)); + if (!SUCCEEDED(hr)) { + GHOST_PRINT("Failed to register scroll direction change listener\n"); + return; + } +} + +void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId) +{ + [[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n"); + + if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) { + m_isScrollDirectionInverted = getScrollDirectionFromReg(); + registerScrollDirectionChangeListener(); + } +} + +void GHOST_DirectManipulationHelper::update() +{ + if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING || + m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) { + [[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr); + GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n"); + } +} + +void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi) +{ + m_directManipulationEventHandler->dpi = dpi; +} + +GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo() +{ + GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values; + result.isScrollDirectionInverted = m_isScrollDirectionInverted; + + m_directManipulationEventHandler->accumulated_values = {0, 0, 0}; + return result; +} + +GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper() +{ + HRESULT hr; + hr = m_directManipulationViewport->Stop(); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n"); + + hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n"); + + hr = m_directManipulationViewport->Abandon(); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n"); + + hr = m_directManipulationManager->Deactivate(m_hWnd); + GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n"); + + if (m_scrollDirectionChangeEvent) { + CloseHandle(m_scrollDirectionChangeEvent); + m_scrollDirectionChangeEvent = NULL; + } + if (m_scrollDirectionRegKey) { + RegCloseKey(m_scrollDirectionRegKey); + m_scrollDirectionRegKey = NULL; + } +} + +GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler( + uint16_t dpi) + : accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING) +{ +} + +void GHOST_DirectManipulationViewportEventHandler::resetViewport( + IDirectManipulationViewport *viewport) +{ + if (gesture_state != GESTURE_NONE) { + [[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n"); + } + + gesture_state = GESTURE_NONE; + + last_scale = PINCH_SCALE_FACTOR; + last_x = 0.0f; + last_y = 0.0f; +} + +HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged( + IDirectManipulationViewport *viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) +{ + dm_status = current; + + if (current == previous) { + return S_OK; + } + + if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY || + (previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) { + resetViewport(viewport); + } + + return S_OK; +} + +HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated( + IDirectManipulationViewport *viewport) +{ + /* Nothing to do here. */ + return S_OK; +} + +HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated( + IDirectManipulationViewport *viewport, IDirectManipulationContent *content) +{ + float transform[6]; + HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform)); + GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n"); + + const float device_scale_factor = dpi / 96.0f; + + const float scale = transform[0] * PINCH_SCALE_FACTOR; + const float x = transform[4] / device_scale_factor; + const float y = transform[5] / device_scale_factor; + + const float EPS = 3e-5; + + /* Ignore repeating or incorrect input. */ + if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) || + scale == 0.0f) { + GHOST_PRINT("Ignoring touchpad input\n"); + return hr; + } + + /* Assume that every gesture is a pan in the beginning. + * If it's a pinch, the gesture will be changed below. */ + if (gesture_state == GESTURE_NONE) { + gesture_state = GESTURE_PAN; + } + + /* DM doesn't always immediately recognize pinch gestures, + * so allow transition from pan to pinch. */ + if (gesture_state == GESTURE_PAN) { + if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) { + gesture_state = GESTURE_PINCH; + } + } + + /* This state machine is used here because: + * 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time + * because XY transform values become nonsensical during pinch gesture. + * 2. GHOST requires delta values for events while DM provides transformation matrix of the + * current gesture. + * 3. GHOST events accept integer values while DM values are non-integer. + * Truncated fractional parts are accumulated and accounted for in following updates. + */ + switch (gesture_state) { + case GESTURE_PINCH: { + int32_t dscale = roundf(scale - last_scale); + + last_scale += dscale; + + accumulated_values.scale += dscale; + break; + } + case GESTURE_PAN: { + int32_t dx = roundf(x - last_x); + int32_t dy = roundf(y - last_y); + + last_x += dx; + last_y += dy; + + accumulated_values.x += dx; + accumulated_values.y += dy; + break; + } + case GESTURE_NONE: + break; + } + + return hr; +} diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.h b/intern/ghost/intern/GHOST_TrackpadWin32.h new file mode 100644 index 00000000000..2e28f756965 --- /dev/null +++ b/intern/ghost/intern/GHOST_TrackpadWin32.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + * Declaration of GHOST DirectManipulation classes. + */ + +#pragma once + +#ifndef WIN32 +# error WIN32 only! +#endif // WIN32 + +#include "GHOST_Types.h" + +#include <directmanipulation.h> +#include <wrl.h> + +#define PINCH_SCALE_FACTOR 125.0f + +typedef struct { + int32_t x, y, scale; + bool isScrollDirectionInverted; +} GHOST_TTrackpadInfo; + +class GHOST_DirectManipulationHelper; + +class GHOST_DirectManipulationViewportEventHandler + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::Implements< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::FtmBase, + IDirectManipulationViewportEventHandler>> { + public: + GHOST_DirectManipulationViewportEventHandler(uint16_t dpi); + + /* + * Resets viewport and tracked touchpad state. + */ + void resetViewport(IDirectManipulationViewport *viewport); + + /* DirectManipulation callbacks. */ + HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) override; + + HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override; + + HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport, + IDirectManipulationContent *content) override; + + private: + enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state; + + int32_t last_x, last_y, last_scale; + GHOST_TTrackpadInfo accumulated_values; + uint16_t dpi; + DIRECTMANIPULATION_STATUS dm_status; + + friend class GHOST_DirectManipulationHelper; +}; + +class GHOST_DirectManipulationHelper { + public: + /* + * Creates a GHOST_DirectManipulationHelper for the provided window. + * \param hWnd: The window receiving DirectManipulation events. + * \param dpi: The current DPI. + * \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an + * error. + */ + static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi); + + ~GHOST_DirectManipulationHelper(); + + /* + * Drives the DirectManipulation context. + * DirectManipulation's intended use is to tie user input into DirectComposition's compositor + * scaling and translating. We are not using DirectComposition and therefore must drive + * DirectManipulation manually. + */ + void update(); + + /* + * Sets pointer in contact with the DirectManipulation context. + * \param pointerId: ID of the pointer in contact. + */ + void onPointerHitTest(UINT32 pointerId); + + /* + * Updates DPI information for touchpad scaling. + * \param dpi: The new DPI. + */ + void setDPI(uint16_t dpi); + + /* + * Retrieves trackpad input. + * \return The accumulated trackpad translation and scale since last call. + */ + GHOST_TTrackpadInfo getTrackpadInfo(); + + private: + GHOST_DirectManipulationHelper( + HWND hWnd, + Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager, + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager, + Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport, + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + directManipulationEventHandler, + DWORD directManipulationViewportHandlerCookie, + bool isScrollDirectionInverted); + + /* + * Retrieves the scroll direction from the registry. + * \return True if scroll direction is inverted. + */ + static bool getScrollDirectionFromReg(); + + /* + * Registers listener for registry scroll direction entry changes. + */ + void registerScrollDirectionChangeListener(); + + HWND m_hWnd; + + HKEY m_scrollDirectionRegKey; + HANDLE m_scrollDirectionChangeEvent; + + Microsoft::WRL::ComPtr<IDirectManipulationManager> m_directManipulationManager; + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> m_directManipulationUpdateManager; + Microsoft::WRL::ComPtr<IDirectManipulationViewport> m_directManipulationViewport; + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + m_directManipulationEventHandler; + DWORD m_directManipulationViewportHandlerCookie; + + bool m_isScrollDirectionInverted; +}; diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 2ce224b666b..897e6c145da 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -16,9 +16,7 @@ #include "GHOST_ContextWGL.h" -#ifdef WIN32_COMPOSITING -# include <Dwmapi.h> -#endif +#include <Dwmapi.h> #include <assert.h> #include <math.h> @@ -70,6 +68,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_normal_state(GHOST_kWindowStateNormal), m_user32(::LoadLibrary("user32.dll")), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), + m_directManipulationHelper(NULL), m_debug_context(is_debug) { DWORD style = parentwindow ? @@ -172,6 +171,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, break; } + ThemeRefresh(); + ::ShowWindow(m_hWnd, nCmdShow); #ifdef WIN32_COMPOSITING @@ -204,6 +205,42 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, /* Allow the showing of a progress bar on the taskbar. */ CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); + + /* Initialize Direct Manipulation. */ + m_directManipulationHelper = GHOST_DirectManipulationHelper::create(m_hWnd, getDPIHint()); +} + +void GHOST_WindowWin32::updateDirectManipulation() +{ + if (!m_directManipulationHelper) { + return; + } + + m_directManipulationHelper->update(); +} + +void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam) +{ + /* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */ + + if (!m_directManipulationHelper) { + return; + } + + UINT32 pointerId = GET_POINTERID_WPARAM(wParam); + POINTER_INPUT_TYPE pointerType; + if (GetPointerType(pointerId, &pointerType) && pointerType == PT_TOUCHPAD) { + m_directManipulationHelper->onPointerHitTest(pointerId); + } +} + +GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo() +{ + if (!m_directManipulationHelper) { + return {0, 0, 0}; + } + + return m_directManipulationHelper->getTrackpadInfo(); } GHOST_WindowWin32::~GHOST_WindowWin32() @@ -253,6 +290,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32() ::DestroyWindow(m_hWnd); m_hWnd = 0; } + + delete m_directManipulationHelper; + m_directManipulationHelper = NULL; } void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect, @@ -1016,6 +1056,32 @@ GHOST_TabletData GHOST_WindowWin32::getTabletData() } } +void GHOST_WindowWin32::ThemeRefresh() +{ + DWORD lightMode; + DWORD pcbData = sizeof(lightMode); + if (RegGetValueW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\\", + L"AppsUseLightTheme", + RRF_RT_REG_DWORD, + NULL, + &lightMode, + &pcbData) == ERROR_SUCCESS) { + BOOL DarkMode = !lightMode; + + /* 20 == DWMWA_USE_IMMERSIVE_DARK_MODE in Windows 11 SDK. This value was undocumented for + * Windows 10 versions 2004 and later, supported for Windows 11 Build 22000 and later. */ + DwmSetWindowAttribute(this->m_hWnd, 20, &DarkMode, sizeof(DarkMode)); + } +} + +void GHOST_WindowWin32::updateDPI() +{ + if (m_directManipulationHelper) { + m_directManipulationHelper->setDPI(getDPIHint()); + } +} + uint16_t GHOST_WindowWin32::getDPIHint() { if (m_user32) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index d5f47871aff..c958a89ac48 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -13,6 +13,7 @@ #endif // WIN32 #include "GHOST_TaskbarWin32.h" +#include "GHOST_TrackpadWin32.h" #include "GHOST_Window.h" #include "GHOST_Wintab.h" #ifdef WITH_INPUT_IME @@ -286,6 +287,8 @@ class GHOST_WindowWin32 : public GHOST_Window { return GHOST_kFailure; } + void updateDPI(); + uint16_t getDPIHint() override; /** True if the mouse is either over or captured by the window. */ @@ -294,6 +297,9 @@ class GHOST_WindowWin32 : public GHOST_Window { /** True if the window currently resizing. */ bool m_inLiveResize; + /** Called when OS colors change and when the window is created. */ + void ThemeRefresh(); + #ifdef WITH_INPUT_IME GHOST_ImeWin32 *getImeInput() { @@ -305,6 +311,19 @@ class GHOST_WindowWin32 : public GHOST_Window { void endIME(); #endif /* WITH_INPUT_IME */ + /* + * Drive DirectManipulation context. + */ + void updateDirectManipulation(); + + /* + * Handle DM_POINTERHITTEST events. + * \param wParam: wParam from the event. + */ + void onPointerHitTest(WPARAM wParam); + + GHOST_TTrackpadInfo getTrackpadInfo(); + private: /** * \param type: The type of rendering context create. @@ -388,6 +407,8 @@ class GHOST_WindowWin32 : public GHOST_Window { HWND m_parentWindowHwnd; + GHOST_DirectManipulationHelper *m_directManipulationHelper; + #ifdef WITH_INPUT_IME /** Handle input method editors event */ GHOST_ImeWin32 m_imeInput; diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 6f0216176d7..f8164d1f646 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -13658,6 +13658,38 @@ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> </g> <g + id="g17058" + transform="translate(-20.839982,-20.882701)" + style="display:inline;enable-background:new"> + <g + id="g7978"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.928338;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 373.21906,563.36205 c -2.04533,0 -3.71335,1.66802 -3.71335,3.71335 0,2.04534 1.66802,3.71336 3.71335,3.71336 2.04534,0 3.71336,-1.66802 3.71336,-3.71336 0,-2.04533 -1.66802,-3.71335 -3.71336,-3.71335 z" + id="path7726" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:922.783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 364.3828,556.88974 c -0.8999,0 -1.63379,0.73389 -1.63379,1.6338 0,0.89988 0.73389,1.6338 1.63379,1.6338 0.89991,0 1.6338,-0.7339 1.6338,-1.6338 0,-0.8999 -0.73389,-1.6338 -1.6338,-1.6338 z" + id="path7808" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.85;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:922.783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 366.50464,561.82783 c -1.31395,0 -2.38552,1.07155 -2.38552,2.3855 0,1.31395 1.07157,2.38555 2.38552,2.38555 1.31396,0 2.38553,-1.07158 2.38553,-2.38555 0,-1.31395 -1.07157,-2.3855 -2.38553,-2.3855 z" + id="path7890" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1881.46;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 368.88012,557.69285 c -1.06196,0 -1.928,0.86602 -1.928,1.92801 0,1.06192 0.86604,1.92798 1.928,1.92798 1.06195,0 1.92801,-0.86606 1.92801,-1.92798 0,-1.06195 -0.86606,-1.92801 -1.92801,-1.92801 z" + id="path7972" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + </g> + </g> + <g id="g7662" style="fill:#ffffff" transform="matrix(1,0,0,-1,0,1085.9844)"> diff --git a/release/datafiles/blender_icons16/icon16_geometry_nodes.dat b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat Binary files differnew file mode 100644 index 00000000000..195a5b9d6d9 --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat diff --git a/release/datafiles/blender_icons32/icon32_geometry_nodes.dat b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat Binary files differnew file mode 100644 index 00000000000..50971291fcc --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 2e715d54178e24ea463c7bc0cdeb27c682c39ec +Subproject fb1eac2ec80c0adee69990a5386b74a5bd4ca00 diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 599a8db33c45c2ad94f8d482f01b28125279977 +Subproject 67e43f9083b79b33f5aa6ac4d1630946804d953 diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 87d54213d1b..604a577eec9 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -341,7 +341,8 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): msgsrc = "bpy.types." + bl_rna.identifier msgctxt = bl_rna.translation_context or default_context - if bl_rna.name and (bl_rna.name != bl_rna.identifier or msgctxt != default_context): + if bl_rna.name and (bl_rna.name != bl_rna.identifier or + (msgctxt != default_context and not hasattr(bl_rna, 'bl_label'))): process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports, check_ctxt_rna, settings) if bl_rna.description: diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py index 1af9048ebfd..15a8d00cddc 100644 --- a/release/scripts/modules/bpy_extras/__init__.py +++ b/release/scripts/modules/bpy_extras/__init__.py @@ -16,4 +16,5 @@ __all__ = ( "mesh_utils", "node_utils", "view3d_utils", + "id_map_utils", ) diff --git a/release/scripts/modules/bpy_extras/id_map_utils.py b/release/scripts/modules/bpy_extras/id_map_utils.py new file mode 100644 index 00000000000..cf39f2185c6 --- /dev/null +++ b/release/scripts/modules/bpy_extras/id_map_utils.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# <pep8 compliant> + +from typing import Dict, Set +import bpy +from bpy.types import ID + + +__all__ = ( + "get_id_reference_map", + "get_all_referenced_ids", +) + + +def get_id_reference_map() -> Dict[ID, Set[ID]]: + """Return a dictionary of direct datablock references for every datablock in the blend file.""" + inv_map = {} + for key, values in bpy.data.user_map().items(): + for value in values: + if value == key: + # So an object is not considered to be referencing itself. + continue + inv_map.setdefault(value, set()).add(key) + return inv_map + + +def recursive_get_referenced_ids( + ref_map: Dict[ID, Set[ID]], id: ID, referenced_ids: Set, visited: Set +): + """Recursively populate referenced_ids with IDs referenced by id.""" + if id in visited: + # Avoid infinite recursion from circular references. + return + visited.add(id) + for ref in ref_map.get(id, []): + referenced_ids.add(ref) + recursive_get_referenced_ids( + ref_map=ref_map, id=ref, referenced_ids=referenced_ids, visited=visited + ) + + +def get_all_referenced_ids(id: ID, ref_map: Dict[ID, Set[ID]]) -> Set[ID]: + """Return a set of IDs directly or indirectly referenced by id.""" + referenced_ids = set() + recursive_get_referenced_ids( + ref_map=ref_map, id=id, referenced_ids=referenced_ids, visited=set() + ) + return referenced_ids diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index d7fb5673ff5..3e0d3dadcf1 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1300,6 +1300,8 @@ def km_uv_editor(params): {"properties": [("data_path", 'tool_settings.use_snap_uv')]}), ("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("data_path", 'tool_settings.snap_uv_element')]}), + ("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, + {"properties": [("data_path", 'space_data.show_gizmo')]}), *_template_items_context_menu("IMAGE_MT_uvs_context_menu", params.context_menu_event), ]) @@ -1967,6 +1969,8 @@ def km_image(params): ), ("image.render_border", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), ("image.clear_render_border", {"type": 'B', "value": 'PRESS', "ctrl": True, "alt": True}, None), + ("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, + {"properties": [("data_path", 'space_data.show_gizmo')]}), *_template_items_context_menu("IMAGE_MT_mask_context_menu", params.context_menu_event), ]) @@ -2019,37 +2023,20 @@ def km_node_editor(params): {"items": items}, ) - def node_select_ops(select_mouse): - return [ - ("node.select", {"type": select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "alt": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "alt": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, - {"properties": [("extend", True)]}), - ] - - # Allow node selection with both for RMB select if not params.legacy: + items.extend(_template_node_select(type=params.select_mouse, + value=params.select_mouse_value, select_passthrough=True)) + # Allow node selection with both for RMB select. if params.select_mouse == 'RIGHTMOUSE': - items.extend(node_select_ops('LEFTMOUSE')) - items.extend(node_select_ops('RIGHTMOUSE')) - else: - items.extend(node_select_ops('LEFTMOUSE')) + items.extend(_template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)) else: - items.extend(node_select_ops('RIGHTMOUSE')) + 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": [("extend", True)]}), + {"properties": [("toggle", True)]}), ]) items.extend([ @@ -4784,6 +4771,35 @@ 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)]}), + ("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), + ("node.select", {"type": type, "value": value, "shift": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "ctrl": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "alt": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "ctrl": True, "alt": True}, + {"properties": [("toggle", True)]}), + ] + + if select_passthrough and (value == 'PRESS'): + # Add an additional click item to de-select all other items, + # needed so pass-through is able to de-select other items. + items.append(( + "node.select", + {"type": type, "value": 'CLICK'}, + {"properties": [("deselect_all", True)]}, + )) + + return items + + def _template_uv_select(*, type, value, select_passthrough, legacy): # See: `use_tweak_select_passthrough` doc-string. @@ -6545,10 +6561,8 @@ def km_node_editor_tool_select(params, *, fallback): _fallback_id("Node Tool: Tweak", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else [ - ("node.select", {"type": params.select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", not params.legacy)]}), - ]), + *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else + _template_node_select(type=params.select_mouse, value='PRESS', select_passthrough=True)), ]}, ) @@ -6565,6 +6579,8 @@ def km_node_editor_tool_select_box(params, *, fallback): params.tool_tweak_event), properties=[("tweak", True)], )), + *([] if (params.select_mouse == 'RIGHTMOUSE') else + _template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)), ]}, ) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9a116aa1717..649e09fdfaa 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -769,6 +769,19 @@ def brush_settings(layout, context, brush, popover=False): elif brush.color_type == 'GRADIENT': layout.row().prop(brush, "gradient_fill_mode", expand=True) + elif mode == 'SCULPT_CURVES': + if brush.curves_sculpt_tool == 'ADD': + layout.prop(brush.curves_sculpt_settings, "add_amount") + layout.prop(brush.curves_sculpt_settings, "points_per_curve") + layout.prop(brush.curves_sculpt_settings, "curve_length") + layout.prop(brush.curves_sculpt_settings, "interpolate_length") + layout.prop(brush.curves_sculpt_settings, "interpolate_shape") + layout.prop(brush.curves_sculpt_settings, "interpolate_point_count") + use_frontface = True + elif brush.curves_sculpt_tool == 'GROW_SHRINK': + layout.prop(brush.curves_sculpt_settings, "scale_uniform") + layout.prop(brush.curves_sculpt_settings, "minimum_length") + def brush_shared_settings(layout, context, brush, popover=False): """ Draw simple brush settings that are shared between different paint modes. """ @@ -827,6 +840,7 @@ def brush_shared_settings(layout, context, brush, popover=False): if mode == 'SCULPT_CURVES': size = True strength = True + direction = brush.curves_sculpt_tool == "GROW_SHRINK" ### Draw settings. ### ups = context.scene.tool_settings.unified_paint_settings @@ -925,16 +939,6 @@ def brush_settings_advanced(layout, context, brush, popover=False): col.prop(brush, "use_original_plane", text="Plane") layout.separator() - elif mode == 'SCULPT_CURVES': - if brush.curves_sculpt_tool == 'ADD': - layout.prop(brush.curves_sculpt_settings, "add_amount") - layout.prop(brush.curves_sculpt_settings, "curve_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_shape") - elif brush.curves_sculpt_tool == 'GROW_SHRINK': - layout.prop(brush.curves_sculpt_settings, "scale_uniform") - layout.prop(brush.curves_sculpt_settings, "minimum_length") - # 3D and 2D Texture Paint. elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: capabilities = brush.image_paint_capabilities diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py index c1b213766df..2e7e68f02ef 100644 --- a/release/scripts/startup/bl_ui/properties_physics_field.py +++ b/release/scripts/startup/bl_ui/properties_physics_field.py @@ -77,7 +77,6 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): elif field.type == 'GUIDE': col = flow.column() - col.prop(field, "guide_minimum") col.prop(field, "guide_free") col.prop(field, "falloff_power") col.prop(field, "use_guide_path_add") @@ -88,11 +87,20 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): col = flow.column() col.prop(field, "guide_clump_amount", text="Clumping Amount") col.prop(field, "guide_clump_shape") - col.prop(field, "use_max_distance") - sub = col.column() + col.separator() + + col.prop(field, "guide_minimum", text="Min Distance") + + col = layout.column(align=False, heading="Max Distance") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(field, "use_max_distance", text="") + sub = sub.row(align=True) sub.active = field.use_max_distance - sub.prop(field, "distance_max") + sub.prop(field, "distance_max", text="") + row.prop_decorator(field, "distance_max") elif field.type == 'TEXTURE': col = flow.column() diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 5ceaf8fc644..1f9362f02b5 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -840,7 +840,6 @@ class TEXTURE_PT_colors_ramp(TextureButtonsPanel, TextureColorsPoll, Panel): if is_active: layout.template_color_ramp(tex, "color_ramp", expand=True) else: - layout.alignment = 'RIGHT' layout.label(text="Enable the Color Ramp first") diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 1dd50c979e2..d61055c9024 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -806,6 +806,13 @@ class IMAGE_HT_header(Header): layout.separator_spacer() + # Gizmo toggle & popover. + row = layout.row(align=True) + row.prop(sima, "show_gizmo", icon='GIZMO', text="") + sub = row.row(align=True) + sub.active = sima.show_gizmo + sub.popover(panel="IMAGE_PT_gizmo_display", text="") + # Overlay toggle & popover row = layout.row(align=True) row.prop(overlay, "show_overlays", icon='OVERLAY', text="") @@ -1453,6 +1460,26 @@ class IMAGE_PT_uv_cursor(Panel): col.prop(sima, "cursor_location", text="Location") +class IMAGE_PT_gizmo_display(Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Gizmos" + bl_ui_units_x = 8 + + def draw(self, context): + layout = self.layout + + view = context.space_data + + col = layout.column() + col.label(text="Viewport Gizmos") + col.separator() + + col.active = view.show_gizmo + colsub = col.column() + colsub.prop(view, "show_gizmo_navigate", text="Navigate") + + class IMAGE_PT_overlay(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'HEADER' @@ -1680,6 +1707,7 @@ classes = ( IMAGE_PT_scope_sample, IMAGE_PT_uv_cursor, IMAGE_PT_annotation, + IMAGE_PT_gizmo_display, IMAGE_PT_overlay, IMAGE_PT_overlay_guides, IMAGE_PT_overlay_uv_edit, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 6654053bef4..d4d1e6ace76 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2278,6 +2278,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): ({"property": "use_new_point_cloud_type"}, "T75717"), ({"property": "use_full_frame_compositor"}, "T88150"), ({"property": "enable_eevee_next"}, "T93220"), + ({"property": "use_draw_manager_acquire_lock"}, "T98016"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 9756d9ab3a1..7bac7343bca 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -516,12 +516,14 @@ class _draw_tool_settings_context_mode: layout.prop(brush, "curve_preset") if brush.curves_sculpt_tool == 'ADD': - layout.prop(brush, "use_frontface") + layout.prop(brush, "use_frontface", text="Front Faces Only") layout.prop(brush, "falloff_shape", expand=True) layout.prop(brush.curves_sculpt_settings, "add_amount") + layout.prop(brush.curves_sculpt_settings, "points_per_curve") layout.prop(brush.curves_sculpt_settings, "curve_length") layout.prop(brush.curves_sculpt_settings, "interpolate_length") layout.prop(brush.curves_sculpt_settings, "interpolate_shape") + layout.prop(brush.curves_sculpt_settings, "interpolate_point_count") if brush.curves_sculpt_tool == 'GROW_SHRINK': layout.prop(brush, "direction", expand=True, text="") @@ -1236,24 +1238,23 @@ class VIEW3D_MT_view_viewpoint(Menu): def draw(self, _context): layout = self.layout - i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_text_ctxt) + layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_contexts.editor_view3d) layout.separator() - layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt).type = 'TOP' - layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt).type = 'BOTTOM' + layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_contexts.editor_view3d).type = 'TOP' + layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_contexts.editor_view3d).type = 'BOTTOM' layout.separator() - layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt).type = 'FRONT' - layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt).type = 'BACK' + layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_contexts.editor_view3d).type = 'FRONT' + layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_contexts.editor_view3d).type = 'BACK' layout.separator() - layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt).type = 'RIGHT' - layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt).type = 'LEFT' + layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_contexts.editor_view3d).type = 'RIGHT' + layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_contexts.editor_view3d).type = 'LEFT' class VIEW3D_MT_view_navigation(Menu): @@ -1320,33 +1321,32 @@ class VIEW3D_MT_view_align_selected(Menu): def draw(self, _context): layout = self.layout - i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'TOP' - props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'BOTTOM' layout.separator() - props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'FRONT' - props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'BACK' layout.separator() - props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'RIGHT' - props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'LEFT' @@ -2042,7 +2042,7 @@ class VIEW3D_MT_curve_add(Menu): bl_idname = "VIEW3D_MT_curve_add" bl_label = "Curve" - def draw(self, _context): + def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' @@ -2056,18 +2056,14 @@ class VIEW3D_MT_curve_add(Menu): layout.operator("curve.primitive_nurbs_circle_add", text="Nurbs Circle", icon='CURVE_NCIRCLE') layout.operator("curve.primitive_nurbs_path_add", text="Path", icon='CURVE_PATH') + experimental = context.preferences.experimental + if experimental.use_new_curves_type: + layout.separator() -class VIEW3D_MT_curves_add(Menu): - bl_idname = "VIEW3D_MT_curves_add" - bl_label = "Curves" - - def draw(self, _context): - layout = self.layout - - layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA') - layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA') - layout.operator("object.curves_random_add", text="Random", icon='CURVES_DATA') + if experimental.use_new_curves_tools: + layout.operator("object.curves_random_add", text="Random", icon='CURVES_DATA') class VIEW3D_MT_surface_add(Menu): @@ -2222,8 +2218,6 @@ class VIEW3D_MT_add(Menu): # layout.operator_menu_enum("object.curve_add", "type", text="Curve", icon='OUTLINER_OB_CURVE') layout.menu("VIEW3D_MT_curve_add", icon='OUTLINER_OB_CURVE') - if context.preferences.experimental.use_new_curves_type: - layout.menu("VIEW3D_MT_curves_add", icon='OUTLINER_OB_CURVES') # layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META') @@ -3154,7 +3148,9 @@ class VIEW3D_MT_sculpt_curves(Menu): def draw(self, _context): layout = self.layout - layout.operator("curves.snap_curves_to_surface") + layout.operator("curves.snap_curves_to_surface", text="Snap to Deformed Surface").attach_mode = 'DEFORM' + layout.operator("curves.snap_curves_to_surface", text="Snap to Nearest Surface").attach_mode = 'NEAREST' + layout.separator() layout.operator("curves.convert_to_particle_system", text="Convert to Particle System") @@ -7661,7 +7657,6 @@ classes = ( VIEW3D_MT_angle_control, VIEW3D_MT_mesh_add, VIEW3D_MT_curve_add, - VIEW3D_MT_curves_add, VIEW3D_MT_surface_add, VIEW3D_MT_edit_metaball_context_menu, VIEW3D_MT_metaball_add, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index ba02bfb4082..698f0dadc33 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1048,6 +1048,7 @@ class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel): draw = VIEW3D_PT_sculpt_symmetry.draw + class VIEW3D_PT_curves_sculpt_symmetry(Panel, View3DPaintPanel): bl_context = ".curves_sculpt" # dot on purpose (access from topbar) bl_label = "Symmetry" @@ -1069,6 +1070,7 @@ class VIEW3D_PT_curves_sculpt_symmetry(Panel, View3DPaintPanel): row.prop(curves, "use_mirror_y", text="Y", toggle=True) row.prop(curves, "use_mirror_z", text="Z", toggle=True) + class VIEW3D_PT_curves_sculpt_symmetry_for_topbar(Panel): bl_space_type = 'TOPBAR' bl_region_type = 'HEADER' @@ -1077,7 +1079,6 @@ class VIEW3D_PT_curves_sculpt_symmetry_for_topbar(Panel): draw = VIEW3D_PT_curves_sculpt_symmetry.draw - # ********** default tools for weight-paint **************** diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 4f94184f006..817c53cca62 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -419,12 +419,10 @@ shader_node_categories = [ NodeItem("ShaderNodeRGBToBW"), NodeItem("ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll), NodeItem("ShaderNodeVectorMath"), - NodeItem("ShaderNodeSeparateRGB"), - NodeItem("ShaderNodeCombineRGB"), + NodeItem("ShaderNodeSeparateColor"), + NodeItem("ShaderNodeCombineColor"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), - NodeItem("ShaderNodeSeparateHSV"), - NodeItem("ShaderNodeCombineHSV"), NodeItem("ShaderNodeWavelength"), NodeItem("ShaderNodeBlackbody"), ]), @@ -483,14 +481,8 @@ compositor_node_categories = [ NodeItem("CompositorNodePremulKey"), NodeItem("CompositorNodeIDMask"), NodeItem("CompositorNodeRGBToBW"), - NodeItem("CompositorNodeSepRGBA"), - NodeItem("CompositorNodeCombRGBA"), - NodeItem("CompositorNodeSepHSVA"), - NodeItem("CompositorNodeCombHSVA"), - NodeItem("CompositorNodeSepYUVA"), - NodeItem("CompositorNodeCombYUVA"), - NodeItem("CompositorNodeSepYCCA"), - NodeItem("CompositorNodeCombYCCA"), + NodeItem("CompositorNodeSeparateColor"), + NodeItem("CompositorNodeCombineColor"), NodeItem("CompositorNodeSeparateXYZ"), NodeItem("CompositorNodeCombineXYZ"), NodeItem("CompositorNodeSwitchView"), @@ -576,8 +568,8 @@ texture_node_categories = [ NodeItem("TextureNodeCurveRGB"), NodeItem("TextureNodeInvert"), NodeItem("TextureNodeHueSaturation"), - NodeItem("TextureNodeCompose"), - NodeItem("TextureNodeDecompose"), + NodeItem("TextureNodeCombineColor"), + NodeItem("TextureNodeSeparateColor"), ]), TextureNodeCategory("TEX_PATTERN", "Pattern", items=[ NodeItem("TextureNodeChecker"), @@ -629,8 +621,8 @@ geometry_node_categories = [ NodeItem("ShaderNodeMixRGB"), NodeItem("ShaderNodeRGBCurve"), NodeItem("ShaderNodeValToRGB"), - NodeItem("ShaderNodeSeparateRGB"), - NodeItem("ShaderNodeCombineRGB"), + NodeItem("FunctionNodeSeparateColor"), + NodeItem("FunctionNodeCombineColor"), ]), GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ffc4d37f622..d0592e9a405 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -15,8 +15,9 @@ if(WITH_CLANG_TIDY AND NOT MSVC) endif() find_package(ClangTidy REQUIRED) - set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) - set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) + set(CMAKE_C_CLANG_TIDY + ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) endif() add_subdirectory(blender) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index f93cb8b2d64..a170f27d247 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1374,7 +1374,7 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) font->dpi = dpi; } else { - printf("The current font does not support the size, %f and dpi, %u\n", size, dpi); + printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); return false; } } diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 62bce36dda0..9dfcb1a4ad6 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -123,7 +123,7 @@ typedef struct GlyphCacheBLF { /* font size. */ float size; - /* and dpi. */ + /* and DPI. */ unsigned int dpi; bool bold; @@ -264,7 +264,7 @@ typedef struct FontBLF { /* the width to wrap the text, see BLF_WORD_WRAP */ int wrap_width; - /* font dpi (default 72). */ + /* Font DPI (default 72). */ unsigned int dpi; /* font size. */ @@ -276,7 +276,8 @@ typedef struct FontBLF { /* font options. */ int flags; - /* List of glyph caches (GlyphCacheBLF) for this font for size, dpi, bold, italic. + /** + * List of glyph caches (#GlyphCacheBLF) for this font for size, DPI, bold, italic. * Use blf_glyph_cache_acquire(font) and blf_glyph_cache_release(font) to access cache! */ ListBase cache; diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 0d4560207ea..d52fd91ccdd 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -129,6 +129,36 @@ bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap); void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size); void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size); +/** Get the minimum x value of each curve map table. */ +void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping, + float minimums[4]); + +/** Get the reciprocal of the difference between the maximum and the minimum x value of each curve + * map table. Evaluation parameters can be multiplied by this value to be normalized. If the + * difference is zero, 1^8 is returned. */ +void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping, + float dividers[4]); + +/** Compute the slopes at the start and end points of each curve map. The slopes are multiplied by + * the range of the curve map to compensate for parameter normalization. If the slope is vertical, + * 1^8 is returned. */ +void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping, + float start_slopes[4], + float end_slopes[4]); + +/** Check if the curve map at the index is identity, that is, does nothing. A curve map is said to + * be identity if: + * - The curve mapping uses extrapolation. + * - Its range is 1. + * - The slope at its start point is 1. + * - The slope at its end point is 1. + * - The number of points is 2. + * - The start point is at (0, 0). + * - The end point is at (1, 1). + * Note that this could return false even if the curve map is identity, this happens in the case + * when more than 2 points exist in the curve map but all points are collinear. */ +bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index); + /** * Call when you do images etc, needs restore too. also verifies tables. * non-const (these modify the curve). diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index bf2d50f63be..87fa26a4f73 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -50,7 +50,7 @@ struct BasisCache { Vector<int> start_indices; /** - * The result of #check_valid_size_and_order, to avoid retrieving its inputs later on. + * The result of #check_valid_num_and_order, to avoid retrieving its inputs later on. * If this is true, the data above will be invalid, and original data should be copied * to the evaluated result. */ @@ -127,7 +127,7 @@ class CurvesGeometry : public ::CurvesGeometry { * Create curves with the given size. Only the position attribute is created, along with the * offsets. */ - CurvesGeometry(int point_size, int curve_size); + CurvesGeometry(int point_num, int curve_num); CurvesGeometry(const CurvesGeometry &other); CurvesGeometry(CurvesGeometry &&other); CurvesGeometry &operator=(const CurvesGeometry &other); @@ -418,7 +418,7 @@ namespace curves { * The number of segments between control points, accounting for the last segment of cyclic * curves. The logic is simple, but this function should be used to make intentions clearer. */ -inline int curve_segment_size(const int points_num, const bool cyclic) +inline int curve_segment_num(const int points_num, const bool cyclic) { BLI_assert(points_num > 0); return (cyclic && points_num > 1) ? points_num : points_num - 1; @@ -585,11 +585,11 @@ namespace catmull_rom { * \param points_num: The number of points in the curve. * \param resolution: The resolution for each segment. */ -int calculate_evaluated_size(int points_num, bool cyclic, int resolution); +int calculate_evaluated_num(int points_num, bool cyclic, int resolution); /** * Evaluate the Catmull Rom curve. The length of the #dst span should be calculated with - * #calculate_evaluated_size and is expected to divide evenly by the #src span's segment size. + * #calculate_evaluated_num and is expected to divide evenly by the #src span's segment size. */ void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst); @@ -606,7 +606,7 @@ namespace nurbs { /** * Checks the conditions that a NURBS curve needs to evaluate. */ -bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); +bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); /** * Calculate the standard evaluated size for a NURBS curve, using the standard that @@ -616,7 +616,7 @@ bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, Knots * for predictability and so that cached basis weights of NURBS curves with these properties can be * shared. */ -int calculate_evaluated_size( +int calculate_evaluated_num( int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); /** @@ -624,7 +624,7 @@ int calculate_evaluated_size( * The knots must be longer for a cyclic curve, for example, in order to provide weights for the * last evaluated points that are also influenced by the first control points. */ -int knots_size(int points_num, int8_t order, bool cyclic); +int knots_num(int points_num, int8_t order, bool cyclic); /** * Calculate the knots for a curve given its properties, based on built-in standards defined by @@ -644,7 +644,7 @@ void calculate_knots( * and a weight for each control point. */ void calculate_basis_cache(int points_num, - int evaluated_size, + int evaluated_num, int8_t order, bool cyclic, Span<float> knots, @@ -671,6 +671,7 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, } // namespace curves Curves *curves_new_nomain(int points_num, int curves_num); +Curves *curves_new_nomain(CurvesGeometry curves); /** * Create a new curves data-block containing a single curve with the given length and type. @@ -685,11 +686,11 @@ std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &typ inline int CurvesGeometry::points_num() const { - return this->point_size; + return this->point_num; } inline int CurvesGeometry::curves_num() const { - return this->curve_size; + return this->curve_num; } inline IndexRange CurvesGeometry::points_range() const { @@ -719,7 +720,7 @@ inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts inline IndexRange CurvesGeometry::points_for_curve(const int index) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[index]; const int offset_next = this->curve_offsets[index + 1]; @@ -729,7 +730,7 @@ inline IndexRange CurvesGeometry::points_for_curve(const int index) const inline IndexRange CurvesGeometry::points_for_curves(const IndexRange curves) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[curves.start()]; const int offset_next = this->curve_offsets[curves.one_after_last()]; @@ -751,7 +752,7 @@ inline IndexRange CurvesGeometry::evaluated_points_for_curve(int index) const inline IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange curves) const { BLI_assert(!this->runtime->offsets_cache_dirty); - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); const int offset = this->runtime->evaluated_offsets_cache[curves.start()]; const int offset_next = this->runtime->evaluated_offsets_cache[curves.one_after_last()]; return {offset, offset_next - offset}; @@ -769,7 +770,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, BLI_assert(cyclic == this->cyclic()[curve_index]); const IndexRange points = this->evaluated_points_for_curve(curve_index); const int start = points.start() + curve_index; - return {start, curves::curve_segment_size(points.size(), cyclic)}; + return {start, curves::curve_segment_num(points.size(), cyclic)}; } inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh new file mode 100644 index 00000000000..62b060093e9 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.hh" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +namespace blender::bke::curves { + +/** + * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. + */ +void fill_curve_counts(const bke::CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + MutableSpan<int> counts); + +/** + * Turn an array of sizes into the offset at each index including all previous sizes. + */ +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0); + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 36b382feb5f..9c86ab262ef 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -162,4 +162,14 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override; }; +class CurveLengthFieldInput final : public GeometryFieldInput { + public: + CurveLengthFieldInput(); + GVArray get_varray_for_context(const GeometryComponent &component, + AttributeDomain domain, + IndexMask mask) const final; + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index dfd9fccebbd..849c430fd7b 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -98,7 +98,7 @@ class GeometryComponent { /** * Return the length of a specific domain, or 0 if the domain is not supported. */ - virtual int attribute_domain_size(AttributeDomain domain) const; + virtual int attribute_domain_num(AttributeDomain domain) const; /** * Return true if the attribute name corresponds to a built-in attribute with a hardcoded domain @@ -560,7 +560,7 @@ class MeshComponent : public GeometryComponent { */ Mesh *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -623,7 +623,7 @@ class PointCloudComponent : public GeometryComponent { */ PointCloud *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -664,7 +664,7 @@ class CurveComponentLegacy : public GeometryComponent { const CurveEval *get_for_read() const; CurveEval *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -715,7 +715,7 @@ class CurveComponent : public GeometryComponent { const Curves *get_for_read() const; Curves *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -949,8 +949,8 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan<blender::float4x4> instance_transforms(); blender::Span<blender::float4x4> instance_transforms() const; - int instances_amount() const; - int references_amount() const; + int instances_num() const; + int references_num() const; /** * Remove the indices that are not contained in the mask input, and remove unused instance @@ -963,7 +963,7 @@ class InstancesComponent : public GeometryComponent { blender::bke::CustomDataAttributes &attributes(); const blender::bke::CustomDataAttributes &attributes() const; - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; void foreach_referenced_geometry( blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 57afbefd508..f62225179bd 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -57,7 +57,9 @@ enum { ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3, /** Do not remap library override pointers. */ ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5, - /** Don't touch the user count (use for low level actions such as swapping pointers). */ + /** Don't touch the special user counts (use when the 'old' remapped ID remains in use): + * - Do not transfer 'fake user' status from old to new ID. + * - Do not clear 'extra user' from old ID. */ ID_REMAP_SKIP_USER_CLEAR = 1 << 6, /** * Force internal ID runtime pointers (like `ID.newid`, `ID.orig_id` etc.) to also be processed. diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index f38f6afff7e..05e502f06c2 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -52,7 +52,7 @@ void BKE_object_material_remap_calc(struct Object *ob_dst, */ void BKE_object_material_from_eval_data(struct Main *bmain, struct Object *ob_orig, - struct ID *data_eval); + const struct ID *data_eval); struct Material *BKE_material_add(struct Main *bmain, const char *name); struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name); void BKE_gpencil_material_attr_init(struct Material *ma); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index bd9bacb52c1..091f30825ae 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -893,6 +893,14 @@ struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh, int tot_vtargetmap, int merge_mode); +/** + * Account for custom-data such as UV's becoming detached because of of imprecision + * in custom-data interpolation. + * Without running this operation subdivision surface can cause UV's to be disconnected, + * see: T81065. + */ +void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me); + /* Flush flags. */ /** diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index a38bfb497a2..4749aab4379 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -102,6 +102,7 @@ typedef enum { /** Accepts #BMesh input (without conversion). */ eModifierTypeFlag_AcceptsBMesh = (1 << 11), } ModifierTypeFlag; +ENUM_OPERATORS(ModifierTypeFlag, eModifierTypeFlag_AcceptsBMesh) typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); typedef void (*TexWalkFunc)(void *userData, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c9228db9ecc..1ff10d06b00 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1090,8 +1090,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_SQUEEZE 117 //#define SH_NODE_MATERIAL_EXT 118 #define SH_NODE_INVERT 119 -#define SH_NODE_SEPRGB 120 -#define SH_NODE_COMBRGB 121 +#define SH_NODE_SEPRGB_LEGACY 120 +#define SH_NODE_COMBRGB_LEGACY 121 #define SH_NODE_HUE_SAT 122 #define SH_NODE_OUTPUT_MATERIAL 124 @@ -1147,8 +1147,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_WAVELENGTH 180 #define SH_NODE_BLACKBODY 181 #define SH_NODE_VECT_TRANSFORM 182 -#define SH_NODE_SEPHSV 183 -#define SH_NODE_COMBHSV 184 +#define SH_NODE_SEPHSV_LEGACY 183 +#define SH_NODE_COMBHSV_LEGACY 184 #define SH_NODE_BSDF_HAIR 185 // #define SH_NODE_LAMP 186 #define SH_NODE_UVMAP 187 @@ -1175,6 +1175,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_VECTOR_ROTATE 708 #define SH_NODE_CURVE_FLOAT 709 #define SH_NODE_POINT_INFO 710 +#define SH_NODE_COMBINE_COLOR 711 +#define SH_NODE_SEPARATE_COLOR 712 /** \} */ @@ -1202,8 +1204,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_MAP_VALUE 213 #define CMP_NODE_TIME 214 #define CMP_NODE_VECBLUR 215 -#define CMP_NODE_SEPRGBA 216 -#define CMP_NODE_SEPHSVA 217 +#define CMP_NODE_SEPRGBA_LEGACY 216 +#define CMP_NODE_SEPHSVA_LEGACY 217 #define CMP_NODE_SETALPHA 218 #define CMP_NODE_HUE_SAT 219 #define CMP_NODE_IMAGE 220 @@ -1213,14 +1215,14 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_TEXTURE 224 #define CMP_NODE_TRANSLATE 225 #define CMP_NODE_ZCOMBINE 226 -#define CMP_NODE_COMBRGBA 227 +#define CMP_NODE_COMBRGBA_LEGACY 227 #define CMP_NODE_DILATEERODE 228 #define CMP_NODE_ROTATE 229 #define CMP_NODE_SCALE 230 -#define CMP_NODE_SEPYCCA 231 -#define CMP_NODE_COMBYCCA 232 -#define CMP_NODE_SEPYUVA 233 -#define CMP_NODE_COMBYUVA 234 +#define CMP_NODE_SEPYCCA_LEGACY 231 +#define CMP_NODE_COMBYCCA_LEGACY 232 +#define CMP_NODE_SEPYUVA_LEGACY 233 +#define CMP_NODE_COMBYUVA_LEGACY 234 #define CMP_NODE_DIFF_MATTE 235 #define CMP_NODE_COLOR_SPILL 236 #define CMP_NODE_CHROMA_MATTE 237 @@ -1232,7 +1234,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_ID_MASK 243 #define CMP_NODE_DEFOCUS 244 #define CMP_NODE_DISPLACE 245 -#define CMP_NODE_COMBHSVA 246 +#define CMP_NODE_COMBHSVA_LEGACY 246 #define CMP_NODE_MATH 247 #define CMP_NODE_LUMA_MATTE 248 #define CMP_NODE_BRIGHTCONTRAST 249 @@ -1289,6 +1291,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_SCENE_TIME 329 #define CMP_NODE_SEPARATE_XYZ 330 #define CMP_NODE_COMBINE_XYZ 331 +#define CMP_NODE_COMBINE_COLOR 332 +#define CMP_NODE_SEPARATE_COLOR 333 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -1354,11 +1358,13 @@ struct TexResult; #define TEX_NODE_TRANSLATE 416 #define TEX_NODE_COORD 417 #define TEX_NODE_DISTANCE 418 -#define TEX_NODE_COMPOSE 419 -#define TEX_NODE_DECOMPOSE 420 +#define TEX_NODE_COMPOSE_LEGACY 419 +#define TEX_NODE_DECOMPOSE_LEGACY 420 #define TEX_NODE_VALTONOR 421 #define TEX_NODE_SCALE 422 #define TEX_NODE_AT 423 +#define TEX_NODE_COMBINE_COLOR 424 +#define TEX_NODE_SEPARATE_COLOR 425 /* 501-599 reserved. Use like this: TEX_NODE_PROC + TEX_CLOUDS, etc */ #define TEX_NODE_PROC 500 @@ -1511,6 +1517,8 @@ struct TexResult; #define FN_NODE_REPLACE_STRING 1218 #define FN_NODE_INPUT_BOOL 1219 #define FN_NODE_INPUT_INT 1220 +#define FN_NODE_SEPARATE_COLOR 1221 +#define FN_NODE_COMBINE_COLOR 1222 /** \} */ diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 6cbb47dc709..28f326a4ad4 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -102,7 +102,7 @@ class Spline { /** Return the number of control points. */ virtual int size() const = 0; - int segments_size() const; + int segments_num() const; bool is_cyclic() const; void set_cyclic(bool value); @@ -127,8 +127,8 @@ class Spline { * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. */ virtual void mark_cache_invalid() = 0; - virtual int evaluated_points_size() const = 0; - int evaluated_edges_size() const; + virtual int evaluated_points_num() const = 0; + int evaluated_edges_num() const; float length() const; @@ -164,7 +164,7 @@ class Spline { /** * The index of the evaluated point after the result location, accounting for wrapping when * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will - * be the last index (#evaluated_points_size - 1). + * be the last index (#evaluated_points_num - 1). */ int next_evaluated_index; /** @@ -191,7 +191,7 @@ class Spline { * indices and factors to the next index encoded in floats. The logic for converting from the * float values to interpolation data is in #lookup_data_from_index_factor. */ - blender::Array<float> sample_uniform_index_factors(int samples_size) const; + blender::Array<float> sample_uniform_index_factors(int samples_num) const; LookupResult lookup_data_from_index_factor(float index_factor) const; /** @@ -344,7 +344,7 @@ class BezierSpline final : public Spline { bool point_is_sharp(int index) const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; /** * Returns access to a cache of offsets into the evaluated point array for each control point. @@ -472,7 +472,7 @@ class NURBSpline final : public Spline { /** * Determines where and how the control points affect the evaluated points. The length should - * always be the value returned by #knots_size(), and each value should be greater than or equal + * always be the value returned by #knots_num(), and each value should be greater than or equal * to the previous. Only invalidated when a point is added or removed. */ mutable blender::Vector<float> knots_; @@ -514,8 +514,8 @@ class NURBSpline final : public Spline { uint8_t order() const; void set_order(uint8_t value); - bool check_valid_size_and_order() const; - int knots_size() const; + bool check_valid_num_and_order() const; + int knots_num() const; void resize(int size) final; blender::MutableSpan<blender::float3> positions() final; @@ -530,7 +530,7 @@ class NURBSpline final : public Spline { blender::Span<float> weights() const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -582,7 +582,7 @@ class PolySpline final : public Spline { blender::Span<float> tilts() const final; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -665,7 +665,7 @@ struct CurveEval { blender::Array<float> accumulated_spline_lengths() const; float total_length() const; - int total_control_point_size() const; + int total_control_point_num() const; void mark_cache_invalid(); diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h index d6de95a19b7..823d32f83ba 100644 --- a/source/blender/blenkernel/BKE_unit.h +++ b/source/blender/blenkernel/BKE_unit.h @@ -91,7 +91,7 @@ const char *BKE_unit_identifier_get(const void *usys_pt, int index); double BKE_unit_scalar_get(const void *usys_pt, int index); bool BKE_unit_is_suppressed(const void *usys_pt, int index); -/** Aligned with #PropertyUnit. */ +/** Aligned with #PropertyUnit and `bpyunits_ucategories_items` in `bpy_utils_units.c`. */ enum { B_UNIT_NONE = 0, B_UNIT_LENGTH = 1, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 5bd89b408de..70ff7035ae7 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -117,6 +117,7 @@ set(SRC intern/curveprofile.cc intern/curves.cc intern/curves_geometry.cc + intern/curves_utils.cc intern/customdata.cc intern/customdata_file.c intern/data_transfer.c @@ -199,6 +200,7 @@ set(SRC intern/mesh_iterators.c intern/mesh_mapping.c intern/mesh_merge.c + intern/mesh_merge_customdata.cc intern/mesh_mirror.c intern/mesh_normals.cc intern/mesh_remap.c @@ -241,8 +243,8 @@ set(SRC intern/particle_child.c intern/particle_distribute.c intern/particle_system.c - intern/pbvh.cc intern/pbvh.c + intern/pbvh.cc intern/pbvh_bmesh.c intern/pbvh_pixels.cc intern/pbvh_pixels_seams.cc @@ -356,6 +358,7 @@ set(SRC BKE_curveprofile.h BKE_curves.h BKE_curves.hh + BKE_curves_utils.hh BKE_customdata.h BKE_customdata_file.h BKE_data_transfer.h diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc index 6ce6bee547c..636e0af0edf 100644 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -41,7 +41,7 @@ static std::string get_new_internal_name() { static std::atomic<int> index = 0; const int next_index = index.fetch_add(1); - return "anonymous_attribute_" + std::to_string(next_index); + return ".a_" + std::to_string(next_index); } AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 11f36e32b74..81eb1786322 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -318,7 +318,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes) const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); ASSERT_NE(nullptr, found_by_id); EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) - << "Backslashes should be normalised when loading from disk."; + << "Backslashes should be normalized when loading from disk."; EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name); const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes"); diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index 0cb0704ff80..7f93eb7b393 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -75,9 +75,9 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) case ID_CV: { Curves *curves = (Curves *)id; info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data; - info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size; + info[ATTR_DOMAIN_POINT].length = curves->geometry.point_num; info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data; - info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size; + info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num; break; } default: diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d33b64c493b..e86bac21084 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -184,16 +184,16 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); return data != nullptr; } case AttributeInit::Type::VArray: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); if (data == nullptr) { return false; } @@ -204,7 +204,7 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = CustomData_add_layer( - &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + &custom_data, data_type, CD_ASSIGN, source_data, domain_num); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -221,35 +221,35 @@ static void *add_generic_custom_data_layer(CustomData &custom_data, const CustomDataType data_type, const eCDAllocType alloctype, void *layer_data, - const int domain_size, + const int domain_num, const AttributeIDRef &attribute_id) { if (attribute_id.is_named()) { char attribute_name_c[MAX_NAME]; attribute_id.name().copy(attribute_name_c); return CustomData_add_layer_named( - &custom_data, data_type, alloctype, layer_data, domain_size, attribute_name_c); + &custom_data, data_type, alloctype, layer_data, domain_num, attribute_name_c); } const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); return CustomData_add_layer_anonymous( - &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); + &custom_data, data_type, alloctype, layer_data, domain_num, &anonymous_id); } static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); return data != nullptr; } case AttributeInit::Type::VArray: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); if (data == nullptr) { return false; } @@ -260,7 +260,7 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); + custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -303,8 +303,8 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent return {}; } - const int domain_size = component.attribute_domain_size(domain_); - return as_read_attribute_(data, domain_size); + const int domain_num = component.attribute_domain_num(domain_); + return as_read_attribute_(data, domain_num); } WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( @@ -317,7 +317,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data; if (stored_as_named_attribute_) { @@ -333,10 +333,10 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( void *new_data; if (stored_as_named_attribute_) { new_data = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, name_.c_str(), domain_size); + custom_data, stored_type_, name_.c_str(), domain_num); } else { - new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num); } if (data != new_data) { @@ -353,7 +353,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( }; } - return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; + return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)}; } bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const @@ -366,7 +366,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); int layer_index; if (stored_as_named_attribute_) { for (const int i : IndexRange(custom_data->totlayer)) { @@ -381,7 +381,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co } const bool delete_success = CustomData_free_layer( - custom_data, stored_type_, domain_size, layer_index); + custom_data, stored_type_, domain_num, layer_index); if (delete_success) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); @@ -401,7 +401,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); bool success; if (stored_as_named_attribute_) { if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { @@ -409,7 +409,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_custom_data_layer_from_attribute_init( - name_, *custom_data, stored_type_, domain_size, initializer); + name_, *custom_data, stored_type_, domain_num, initializer); } else { if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { @@ -417,7 +417,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_size, initializer); + *custom_data, stored_type_, domain_num, initializer); } if (success) { if (custom_data_access_.update_custom_data_pointers) { @@ -446,7 +446,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; @@ -455,7 +455,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (type == nullptr) { continue; } - GSpan data{*type, layer.data, domain_size}; + GSpan data{*type, layer.data, domain_num}; return {GVArray::ForSpan(data), domain_}; } return {}; @@ -468,24 +468,23 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named( - custom_data, layer.type, layer.name, domain_size); + CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_num); } else { CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + custom_data, layer.type, &attribute_id.anonymous_id(), domain_num); } const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); if (type == nullptr) { continue; } - GMutableSpan data{*type, layer.data, domain_size}; + GMutableSpan data{*type, layer.data, domain_num}; return {GVMutableArray::ForSpan(data), domain_}; } return {}; @@ -498,12 +497,12 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, if (custom_data == nullptr) { return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (this->type_is_supported((CustomDataType)layer.type) && custom_data_layer_matches_attribute_id(layer, attribute_id)) { - CustomData_free_layer(custom_data, layer.type, domain_size, i); + CustomData_free_layer(custom_data, layer.type, domain_num, i); return true; } } @@ -531,9 +530,9 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); add_custom_data_layer_from_attribute_init( - attribute_id, *custom_data, data_type, domain_size, initializer); + attribute_id, *custom_data, data_type, domain_num, initializer); return true; } @@ -567,8 +566,8 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - return {as_read_attribute_(layer.data, domain_size), domain_}; + const int domain_num = component.attribute_domain_num(domain_); + return {as_read_attribute_(layer.data, domain_num), domain_}; } } } @@ -585,16 +584,16 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, layer.name, domain_size); + custom_data, stored_type_, layer.name, domain_num); if (data_old != data_new) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } } - return {as_write_attribute_(layer.data, domain_size), domain_}; + return {as_write_attribute_(layer.data, domain_num), domain_}; } } } @@ -612,8 +611,8 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - CustomData_free_layer(custom_data, stored_type_, domain_size, i); + const int domain_num = component.attribute_domain_num(domain_); + CustomData_free_layer(custom_data, stored_type_, domain_num, i); if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } @@ -702,9 +701,9 @@ GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, std::optional<GSpan> attribute = this->get_for_read(attribute_id); if (!attribute) { - const int domain_size = this->size_; + const int domain_num = this->size_; return GVArray::ForSingle( - *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value); + *type, domain_num, (default_value == nullptr) ? type->default_value() : default_value); } if (attribute->type() == *type) { @@ -822,7 +821,7 @@ bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) return providers->supported_domains().contains(domain); } -int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const +int GeometryComponent::attribute_domain_num(const AttributeDomain UNUSED(domain)) const { return 0; } @@ -1157,8 +1156,8 @@ blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef if (default_value == nullptr) { default_value = type->default_value(); } - const int domain_size = this->attribute_domain_size(domain); - return blender::GVArray::ForSingle(*type, domain_size, default_value); + const int domain_num = this->attribute_domain_num(domain); + return blender::GVArray::ForSingle(*type, domain_num, default_value); } class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { @@ -1267,10 +1266,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); component.attribute_try_create_builtin( attribute_name, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); @@ -1301,7 +1300,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, ignore_old_values); } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { @@ -1310,7 +1309,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, attribute_id, domain, data_type, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); @@ -1333,8 +1332,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing * attribute after processing is done. */ - void *data = MEM_mallocN_aligned( - cpp_type->size() * domain_size, cpp_type->alignment(), __func__); + void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__); if (ignore_old_values) { /* This does nothing for trivially constructible types, but is necessary for correctness. */ cpp_type->default_construct_n(data, domain); @@ -1343,10 +1341,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Fill the temporary array with values from the existing attribute. */ GVArray old_varray = component.attribute_get_for_read( attribute_id, domain, data_type, default_value); - old_varray.materialize_to_uninitialized(IndexRange(domain_size), data); + old_varray.materialize_to_uninitialized(IndexRange(domain_num), data); } GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>( - GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); + GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } @@ -1429,7 +1427,7 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c const StringRef name = get_random_id_attribute_name(domain); GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); if (attribute) { - BLI_assert(attribute.size() == component.attribute_domain_size(domain)); + BLI_assert(attribute.size() == component.attribute_domain_num(domain)); return attribute; } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 8c021ed0e21..f0f47cb7a11 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -172,8 +172,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -207,14 +207,14 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; }; -template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size) +template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_num) { - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); + return VArray<T>::ForSpan(Span<T>((const T *)data, domain_num)); } -template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size) +template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_num) { - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); + return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_num)); } /** @@ -226,8 +226,8 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 0593db34e20..e8c95869910 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1559,6 +1559,7 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush) } BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings; settings->add_amount = 1; + settings->points_per_curve = 8; settings->minimum_length = 0.01f; settings->curve_length = 0.3f; } diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index c3d66d4463d..e4c46703f8a 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1158,6 +1158,80 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap) return false; } +void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT]) +{ + for (int i = 0; i < CM_TOT; i++) { + minimums[i] = curve_mapping->cm[i].mintable; + } +} + +void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping, + float dividers[CM_TOT]) +{ + for (int i = 0; i < CM_TOT; i++) { + const CurveMap *curve_map = &curve_mapping->cm[i]; + dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable); + } +} + +void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping, + float start_slopes[CM_TOT], + float end_slopes[CM_TOT]) +{ + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + for (int i = 0; i < CM_TOT; i++) { + const CurveMap *curve_map = &curve_mapping->cm[i]; + /* If extrapolation is not enabled, the slopes are horizontal. */ + if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) { + start_slopes[i] = 0.0f; + end_slopes[i] = 0.0f; + continue; + } + + if (curve_map->ext_in[0] != 0.0f) { + start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]); + } + else { + start_slopes[i] = 1e8f; + } + + if (curve_map->ext_out[0] != 0.0f) { + end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]); + } + else { + end_slopes[i] = 1e8f; + } + } +} + +bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index) +{ + if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) { + return false; + } + const CurveMap *curve_map = &curve_mapping->cm[index]; + if (curve_map->maxtable - curve_map->mintable != 1.0f) { + return false; + } + if (curve_map->ext_in[0] != curve_map->ext_in[1]) { + return false; + } + if (curve_map->ext_out[0] != curve_map->ext_out[1]) { + return false; + } + if (curve_map->totpoint != 2) { + return false; + } + if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) { + return false; + } + if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) { + return false; + } + return true; +} + void BKE_curvemapping_init(CurveMapping *cumap) { int a; diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index 2db183eea3e..4b2174c912c 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -9,11 +9,11 @@ namespace blender::bke::curves::catmull_rom { -int calculate_evaluated_size(const int points_num, const bool cyclic, const int resolution) +int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution) { - const int eval_size = resolution * curve_segment_size(points_num, cyclic); + const int eval_num = resolution * curve_segment_num(points_num, cyclic); /* If the curve isn't cyclic, one last point is added to the final point. */ - return cyclic ? eval_size : eval_size + 1; + return cyclic ? eval_num : eval_num + 1; } /* Adapted from Cycles #catmull_rom_basis_eval function. */ @@ -46,7 +46,7 @@ static void interpolate_to_evaluated(const Span<T> src, MutableSpan<T> dst) { - BLI_assert(dst.size() == calculate_evaluated_size(src.size(), cyclic, resolution)); + BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution)); /* - First deal with one and two point curves need special attention. * - Then evaluate the first and last segment(s) whose control points need to wrap around diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 3d9dd3ecf31..c507e7934a8 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -117,7 +117,7 @@ float CurveEval::total_length() const return length; } -int CurveEval::total_control_point_size() const +int CurveEval::total_control_point_num() const { int count = 0; for (const SplinePtr &spline : this->splines()) { @@ -144,7 +144,7 @@ blender::Array<int> CurveEval::evaluated_point_offsets() const int offset = 0; for (const int i : splines_.index_range()) { offsets[i] = offset; - offset += splines_[i]->evaluated_points_size(); + offset += splines_[i]->evaluated_points_num(); } offsets.last() = offset; return offsets; @@ -463,7 +463,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) Curves *curve_eval_to_curves(const CurveEval &curve_eval) { - Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(), + Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(), curve_eval.splines().size()); CurveComponent dst_component; dst_component.replace(curves, GeometryOwnershipType::Editable); diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 45440358221..cd6b64e9a03 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -10,10 +10,10 @@ namespace blender::bke::curves::nurbs { -bool check_valid_size_and_order(const int points_num, - const int8_t order, - const bool cyclic, - const KnotsMode knots_mode) +bool check_valid_num_and_order(const int points_num, + const int8_t order, + const bool cyclic, + const KnotsMode knots_mode) { if (points_num < order) { return false; @@ -29,19 +29,19 @@ bool check_valid_size_and_order(const int points_num, return true; } -int calculate_evaluated_size(const int points_num, - const int8_t order, - const bool cyclic, - const int resolution, - const KnotsMode knots_mode) +int calculate_evaluated_num(const int points_num, + const int8_t order, + const bool cyclic, + const int resolution, + const KnotsMode knots_mode) { - if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) { + if (!check_valid_num_and_order(points_num, order, cyclic, knots_mode)) { return points_num; } - return resolution * curve_segment_size(points_num, cyclic); + return resolution * curve_segment_num(points_num, cyclic); } -int knots_size(const int points_num, const int8_t order, const bool cyclic) +int knots_num(const int points_num, const int8_t order, const bool cyclic) { if (cyclic) { return points_num + order * 2 - 1; @@ -55,7 +55,7 @@ void calculate_knots(const int points_num, const bool cyclic, MutableSpan<float> knots) { - BLI_assert(knots.size() == knots_size(points_num, order, cyclic)); + BLI_assert(knots.size() == knots_num(points_num, order, cyclic)); UNUSED_VARS_NDEBUG(points_num); const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); @@ -147,7 +147,7 @@ static void calculate_basis_for_point(const float parameter, } void calculate_basis_cache(const int points_num, - const int evaluated_size, + const int evaluated_num, const int8_t order, const bool cyclic, const Span<float> knots, @@ -157,10 +157,10 @@ void calculate_basis_cache(const int points_num, const int8_t degree = order - 1; - basis_cache.weights.resize(evaluated_size * order); - basis_cache.start_indices.resize(evaluated_size); + basis_cache.weights.resize(evaluated_num * order); + basis_cache.start_indices.resize(evaluated_num); - if (evaluated_size == 0) { + if (evaluated_num == 0) { return; } @@ -168,12 +168,12 @@ void calculate_basis_cache(const int points_num, MutableSpan<int> basis_start_indices(basis_cache.start_indices); const int last_control_point_index = cyclic ? points_num + degree : points_num; - const int evaluated_segment_size = curve_segment_size(evaluated_size, cyclic); + const int evaluated_segment_num = curve_segment_num(evaluated_num, cyclic); const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / evaluated_segment_size; - for (const int i : IndexRange(evaluated_size)) { + const float step = (end - start) / evaluated_segment_num; + for (const int i : IndexRange(evaluated_num)) { /* Clamp parameter due to floating point inaccuracy. */ const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]); diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc index 2db7cd71ad3..1b337379604 100644 --- a/source/blender/blenkernel/intern/curve_poly.cc +++ b/source/blender/blenkernel/intern/curve_poly.cc @@ -110,7 +110,7 @@ void calculate_normals_minimum(const Span<float3> tangents, normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f)); } - /* Forward normal with minimum twist along the entire spline. */ + /* Forward normal with minimum twist along the entire curve. */ for (const int i : IndexRange(1, normals.size() - 1)) { normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]); } @@ -120,7 +120,7 @@ void calculate_normals_minimum(const Span<float3> tangents, } /* Compute how much the first normal deviates from the normal that has been forwarded along the - * entire cyclic spline. */ + * entire cyclic curve. */ const float3 uncorrected_last_normal = calculate_next_normal( normals.last(), tangents.last(), tangents.first()); float correction_angle = angle_signed_on_axis_v3v3_v3( diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index ef921797698..0cd324cfe2c 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -39,8 +39,8 @@ static void fill_mesh_topology(const int vert_offset, MutableSpan<MLoop> loops, MutableSpan<MPoly> polys) { - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); if (profile_point_num == 1) { for (const int i : IndexRange(main_point_num - 1)) { @@ -134,9 +134,9 @@ static void fill_mesh_topology(const int vert_offset, const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; if (has_caps) { - const int poly_size = main_segment_num * profile_segment_num; - const int cap_loop_offset = loop_offset + poly_size * 4; - const int cap_poly_offset = poly_offset + poly_size; + const int poly_num = main_segment_num * profile_segment_num; + const int cap_loop_offset = loop_offset + poly_num * 4; + const int cap_poly_offset = poly_offset + poly_num; MPoly &poly_start = polys[cap_poly_offset]; poly_start.loopstart = cap_loop_offset; @@ -273,7 +273,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool for (const int i_main : info.main.curves_range()) { const bool main_cyclic = info.main_cyclic[i_main]; const int main_point_num = info.main.evaluated_points_for_curve(i_main).size(); - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); for (const int i_profile : info.profile.curves_range()) { result.vert[mesh_index] = vert_offset; result.edge[mesh_index] = edge_offset; @@ -285,8 +285,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool const bool profile_cyclic = info.profile_cyclic[i_profile]; const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size(); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, - profile_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; const int tube_face_num = main_segment_num * profile_segment_num; @@ -408,8 +407,8 @@ static void foreach_curve_combination(const CurvesInfo &info, profile_points, main_cyclic, profile_cyclic, - curves::curve_segment_size(main_points.size(), main_cyclic), - curves::curve_segment_size(profile_points.size(), profile_cyclic), + curves::curve_segment_num(main_points.size(), main_cyclic), + curves::curve_segment_num(profile_points.size(), profile_cyclic), offsets_to_range(offsets.vert.as_span(), i), offsets_to_range(offsets.edge.as_span(), i), offsets_to_range(offsets.poly.as_span(), i), diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 1df1492bac1..84ba98db54b 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -78,12 +78,12 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, * shallow copy from the source to the destination, and because the copy-on-write functionality * isn't supported more generically yet. */ - dst.point_size = src.point_size; - dst.curve_size = src.curve_size; + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size); - CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size); + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_num); dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets)); @@ -136,17 +136,17 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre CustomData_blend_write(writer, &curves->geometry.point_data, players, - curves->geometry.point_size, + curves->geometry.point_num, CD_MASK_ALL, &curves->id); CustomData_blend_write(writer, &curves->geometry.curve_data, clayers, - curves->geometry.curve_size, + curves->geometry.curve_num, CD_MASK_ALL, &curves->id); - BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets); + BLO_write_int32_array(writer, curves->geometry.curve_num + 1, curves->geometry.curve_offsets); BLO_write_pointer_array(writer, curves->totcol, curves->mat); if (curves->adt) { @@ -169,11 +169,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) BKE_animdata_blend_read_data(reader, curves->adt); /* Geometry */ - CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); - CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size); + CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); + CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); update_custom_data_pointers(*curves); - BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets); + BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__); @@ -379,4 +379,11 @@ Curves *curves_new_nomain_single(const int points_num, const CurveType type) return curves; } +Curves *curves_new_nomain(CurvesGeometry curves) +{ + Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + bke::CurvesGeometry::wrap(curves_id->geometry) = std::move(curves); + return curves_id; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index e7337d5c012..0fd58a52f81 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -46,10 +46,10 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) { } -CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) +CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) { - this->point_size = point_size; - this->curve_size = curve_size; + this->point_num = point_num; + this->curve_num = curve_num; CustomData_reset(&this->point_data); CustomData_reset(&this->curve_data); @@ -57,14 +57,16 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) CD_PROP_FLOAT3, CD_DEFAULT, nullptr, - this->point_size, + this->point_num, ATTR_POSITION.c_str()); - this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__); + this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_num + 1, sizeof(int), __func__); this->update_customdata_pointers(); this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); + /* Fill the type counts with the default so they're in a valid state. */ + this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num; } /** @@ -72,15 +74,15 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) */ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) { - CustomData_free(&dst.point_data, dst.point_size); - CustomData_free(&dst.curve_data, dst.curve_size); - dst.point_size = src.point_size; - dst.curve_size = src.curve_size; - CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size); - CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size); + CustomData_free(&dst.point_data, dst.point_num); + CustomData_free(&dst.curve_data, dst.curve_num); + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num); MEM_SAFE_FREE(dst.curve_offsets); - dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__); + dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_num + 1, sizeof(int), __func__); dst.offsets_for_write().copy_from(src.offsets()); dst.tag_topology_changed(); @@ -92,7 +94,7 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { copy_curves_geometry(*this, other); } @@ -108,15 +110,15 @@ CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) /* The source should be empty, but in a valid state so that using it further will work. */ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) { - dst.point_size = src.point_size; + dst.point_num = src.point_num; std::swap(dst.point_data, src.point_data); - CustomData_free(&src.point_data, src.point_size); - src.point_size = 0; + CustomData_free(&src.point_data, src.point_num); + src.point_num = 0; - dst.curve_size = src.curve_size; + dst.curve_num = src.curve_num; std::swap(dst.curve_data, src.curve_data); - CustomData_free(&src.curve_data, src.curve_size); - src.curve_size = 0; + CustomData_free(&src.curve_data, src.curve_num); + src.curve_num = 0; std::swap(dst.curve_offsets, src.curve_offsets); MEM_SAFE_FREE(src.curve_offsets); @@ -128,7 +130,7 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { move_curves_geometry(*this, other); } @@ -143,8 +145,8 @@ CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other) CurvesGeometry::~CurvesGeometry() { - CustomData_free(&this->point_data, this->point_size); - CustomData_free(&this->curve_data, this->curve_size); + CustomData_free(&this->point_data, this->point_num); + CustomData_free(&this->curve_data, this->curve_num); MEM_SAFE_FREE(this->curve_offsets); MEM_delete(this->runtime); this->runtime = nullptr; @@ -156,7 +158,7 @@ CurvesGeometry::~CurvesGeometry() /** \name Accessors * \{ */ -static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) +static int domain_num(const CurvesGeometry &curves, const AttributeDomain domain) { return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num(); } @@ -178,15 +180,15 @@ static VArray<T> get_varray_attribute(const CurvesGeometry &curves, const StringRefNull name, const T default_value) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); const CustomData &custom_data = domain_custom_data(curves, domain); const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str()); if (data != nullptr) { - return VArray<T>::ForSpan(Span<T>(data, size)); + return VArray<T>::ForSpan(Span<T>(data, num)); } - return VArray<T>::ForSingle(default_value, size); + return VArray<T>::ForSingle(default_value, num); } template<typename T> @@ -194,7 +196,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, const AttributeDomain domain, const StringRefNull name) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomData &custom_data = domain_custom_data(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); @@ -202,7 +204,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, if (data == nullptr) { return {}; } - return {data, size}; + return {data, num}; } template<typename T> @@ -211,19 +213,19 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves, const StringRefNull name, const T default_value = T()) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); CustomData &custom_data = domain_custom_data(curves, domain); T *data = (T *)CustomData_duplicate_referenced_layer_named( - &custom_data, type, name.c_str(), size); + &custom_data, type, name.c_str(), num); if (data != nullptr) { - return {data, size}; + return {data, num}; } data = (T *)CustomData_add_layer_named( - &custom_data, type, CD_CALLOC, nullptr, size, name.c_str()); - MutableSpan<T> span = {data, size}; - if (size > 0 && span.first() != default_value) { + &custom_data, type, CD_CALLOC, nullptr, num, name.c_str()); + MutableSpan<T> span = {data, num}; + if (num > 0 && span.first() != default_value) { span.fill(default_value); } return span; @@ -299,22 +301,22 @@ void CurvesGeometry::update_curve_types() Span<float3> CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_size}; + return {(const float3 *)this->position, this->point_num}; } MutableSpan<float3> CurvesGeometry::positions_for_write() { this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size); - return {(float3 *)this->position, this->point_size}; + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); + return {(float3 *)this->position, this->point_num}; } Span<int> CurvesGeometry::offsets() const { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } MutableSpan<int> CurvesGeometry::offsets_for_write() { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } VArray<bool> CurvesGeometry::cyclic() const @@ -442,12 +444,12 @@ MutableSpan<float2> CurvesGeometry::surface_triangle_coords_for_write() /** \name Evaluation * \{ */ -template<typename SizeFn> void build_offsets(MutableSpan<int> offsets, const SizeFn &size_fn) +template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn) { int offset = 0; for (const int i : offsets.drop_back(1).index_range()) { offsets[i] = offset; - offset += size_fn(i); + offset += count_fn(i); } offsets.last() = offset; } @@ -470,7 +472,7 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, const IndexRange points = curves.points_for_curve(curve_index); switch (types[curve_index]) { case CURVE_TYPE_CATMULL_ROM: - return curves::catmull_rom::calculate_evaluated_size( + return curves::catmull_rom::calculate_evaluated_num( points.size(), cyclic[curve_index], resolution[curve_index]); case CURVE_TYPE_POLY: return points.size(); @@ -482,11 +484,11 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, bezier_evaluated_offsets.slice(points)); return bezier_evaluated_offsets[points.last()]; case CURVE_TYPE_NURBS: - return curves::nurbs::calculate_evaluated_size(points.size(), - nurbs_orders[curve_index], - cyclic[curve_index], - resolution[curve_index], - KnotsMode(nurbs_knots_modes[curve_index])); + return curves::nurbs::calculate_evaluated_num(points.size(), + nurbs_orders[curve_index], + cyclic[curve_index], + resolution[curve_index], + KnotsMode(nurbs_knots_modes[curve_index])); } BLI_assert_unreachable(); return 0; @@ -541,7 +543,11 @@ IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, if (this->curve_type_counts()[type] == this->curves_num()) { return selection; } - Span<int8_t> types_span = this->curve_types().get_internal_span(); + const VArray<int8_t> types = this->curve_types(); + if (types.is_single()) { + return types.get_internal_single() == type ? IndexMask(this->curves_num()) : IndexMask(0); + } + Span<int8_t> types_span = types.get_internal_span(); return index_mask_ops::find_indices_based_on_predicate( selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; }); } @@ -581,13 +587,13 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const const bool is_cyclic = cyclic[curve_index]; const KnotsMode mode = KnotsMode(knots_modes[curve_index]); - if (!curves::nurbs::check_valid_size_and_order(points.size(), order, is_cyclic, mode)) { + if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) { basis_caches[curve_index].invalid = true; continue; } - const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic); - Array<float> knots(knots_size); + const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic); + Array<float> knots(knots_num); curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); curves::nurbs::calculate_basis_cache(points.size(), evaluated_points.size(), @@ -908,8 +914,8 @@ void CurvesGeometry::ensure_evaluated_lengths() const threading::isolate_task([&]() { /* Use an extra length value for the final cyclic segment for a consistent size * (see comment on #evaluated_length_cache). */ - const int total_size = this->evaluated_points_num() + this->curves_num(); - this->runtime->evaluated_length_cache.resize(total_size); + const int total_num = this->evaluated_points_num() + this->curves_num(); + this->runtime->evaluated_length_cache.resize(total_num); MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache; Span<float3> evaluated_positions = this->evaluated_positions(); @@ -938,13 +944,13 @@ void CurvesGeometry::ensure_evaluated_lengths() const void CurvesGeometry::resize(const int points_num, const int curves_num) { - if (points_num != this->point_size) { + if (points_num != this->point_num) { CustomData_realloc(&this->point_data, points_num); - this->point_size = points_num; + this->point_num = points_num; } - if (curves_num != this->curve_size) { + if (curves_num != this->curve_num) { CustomData_realloc(&this->curve_data, curves_num); - this->curve_size = curves_num; + this->curve_num = curves_num; this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc new file mode 100644 index 00000000000..78c2382b62f --- /dev/null +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_curves_utils.hh" + +namespace blender::bke::curves { + +void fill_curve_counts(const bke::CurvesGeometry &curves, + const Span<IndexRange> curve_ranges, + MutableSpan<int> counts) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { + for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { + threading::parallel_for(curves_range, 4096, [&](IndexRange range) { + for (const int i : range) { + counts[i] = curves.points_for_curve(i).size(); + } + }); + } + }); +} + +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset) +{ + int offset = start_offset; + for (const int i : counts_to_offsets.index_range().drop_back(1)) { + const int count = counts_to_offsets[i]; + BLI_assert(count > 0); + counts_to_offsets[i] = offset; + offset += count; + } + counts_to_offsets.last() = offset; +} + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index f409389e463..a28afc8ddca 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -114,7 +114,7 @@ void CurveComponentLegacy::ensure_owns_direct_data() /** \name Attribute Access Helper Functions * \{ */ -int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const +int CurveComponentLegacy::attribute_domain_num(const AttributeDomain domain) const { if (curve_ == nullptr) { return 0; @@ -251,8 +251,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize(const IndexMask mask, MutableSpan<T> r_span) const final { - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -273,8 +273,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final { T *dst = r_span.data(); - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -415,7 +415,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0; } }; @@ -495,8 +495,8 @@ static void point_attribute_materialize(Span<Span<T>> data, const IndexMask mask, MutableSpan<T> r_span) { - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -541,8 +541,8 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, MutableSpan<T> r_span) { T *dst = r_span.data(); - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -589,13 +589,13 @@ static GVArray varray_from_initializer(const AttributeInit &initializer, case AttributeInit::Type::VArray: return static_cast<const AttributeInitVArray &>(initializer).varray; case AttributeInit::Type::MoveArray: - int total_size = 0; + int total_num = 0; for (const SplinePtr &spline : splines) { - total_size += spline->size(); + total_num += spline->size(); } return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type), static_cast<const AttributeInitMove &>(initializer).data, - total_size)); + total_num)); } BLI_assert_unreachable(); return {}; @@ -1168,7 +1168,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { } return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && - component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index bc9bba3ee2f..b565143d08f 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -237,6 +237,69 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri return nullptr; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Length Field Input + * \{ */ + +static VArray<float> construct_curve_length_gvarray(const CurveComponent &component, + const AttributeDomain domain) +{ + if (!component.has_curves()) { + return {}; + } + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + + curves.ensure_evaluated_lengths(); + + VArray<bool> cyclic = curves.cyclic(); + VArray<float> lengths = VArray<float>::ForFunc( + curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { + return curves.evaluated_length_total_for_curve(index, cyclic[index]); + }); + + if (domain == ATTR_DOMAIN_CURVE) { + return lengths; + } + + if (domain == ATTR_DOMAIN_POINT) { + return component.attribute_try_adapt_domain<float>( + std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + } + + return {}; +} + +CurveLengthFieldInput::CurveLengthFieldInput() + : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") +{ + category_ = Category::Generated; +} + +GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const +{ + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_curve_length_gvarray(curve_component, domain); + } + return {}; +} + +uint64_t CurveLengthFieldInput::hash() const +{ + /* Some random constant hash. */ + return 3549623580; +} + +bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; +} + } // namespace blender::bke /** \} */ @@ -245,7 +308,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +int CurveComponent::attribute_domain_num(const AttributeDomain domain) const { if (curves_ == nullptr) { return 0; diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0dc6f486d28..e56a7ca4dd8 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -79,7 +79,7 @@ void InstancesComponent::add_instance(const int instance_handle, const float4x4 BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - attributes_.reallocate(this->instances_amount()); + attributes_.reallocate(this->instances_num()); } blender::Span<int> InstancesComponent::instance_reference_handles() const @@ -183,7 +183,7 @@ void InstancesComponent::remove_unused_references() using namespace blender; using namespace blender::bke; - const int tot_instances = this->instances_amount(); + const int tot_instances = this->instances_num(); const int tot_references_before = references_.size(); if (tot_instances == 0) { @@ -258,12 +258,12 @@ void InstancesComponent::remove_unused_references() }); } -int InstancesComponent::instances_amount() const +int InstancesComponent::instances_num() const { return instance_transforms_.size(); } -int InstancesComponent::references_amount() const +int InstancesComponent::references_num() const { return references_.size(); } @@ -358,7 +358,7 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const } } else { - almost_unique_ids_.reinitialize(this->instances_amount()); + almost_unique_ids_.reinitialize(this->instances_num()); for (const int i : almost_unique_ids_.index_range()) { almost_unique_ids_[i] = i; } @@ -366,12 +366,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } -int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const +int InstancesComponent::attribute_domain_num(const AttributeDomain domain) const { if (domain != ATTR_DOMAIN_INSTANCE) { return 0; } - return this->instances_amount(); + return this->instances_num(); } blender::bke::CustomDataAttributes &InstancesComponent::attributes() diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index fb39861d3e7..5ac9a03f43c 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -169,7 +169,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, /** \name Attribute Access * \{ */ -int MeshComponent::attribute_domain_size(const AttributeDomain domain) const +int MeshComponent::attribute_domain_num(const AttributeDomain domain) const { if (mesh_ == nullptr) { return 0; @@ -839,20 +839,20 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -static GVArray make_derived_read_attribute(const void *data, const int domain_size) +static GVArray make_derived_read_attribute(const void *data, const int domain_num) { return VArray<ElemT>::template ForDerivedSpan<StructT, GetFunc>( - Span<StructT>((const StructT *)data, domain_size)); + Span<StructT>((const StructT *)data, domain_num)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), void (*SetFunc)(StructT &, ElemT)> -static GVMutableArray make_derived_write_attribute(void *data, const int domain_size) +static GVMutableArray make_derived_write_attribute(void *data, const int domain_num) { return VMutableArray<ElemT>::template ForDerivedSpan<StructT, GetFunc, SetFunc>( - MutableSpan<StructT>((StructT *)data, domain_size)); + MutableSpan<StructT>((StructT *)data, domain_num)); } static float3 get_vertex_position(const MVert &vert) @@ -1160,7 +1160,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 400e0ea5e15..6de123c7cb9 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -104,7 +104,7 @@ void PointCloudComponent::ensure_owns_direct_data() /** \name Attribute Access * \{ */ -int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const +int PointCloudComponent::attribute_domain_num(const AttributeDomain domain) const { if (pointcloud_ == nullptr) { return 0; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 2bd8b643899..40e36ced199 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -272,7 +272,7 @@ bool GeometrySet::has_pointcloud() const bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); - return component != nullptr && component->instances_amount() >= 1; + return component != nullptr && component->instances_num() >= 1; } bool GeometrySet::has_volume() const diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index a2338eb9b39..9dc64365f0c 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1384,7 +1384,21 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } if (id->override_library->hierarchy_root != NULL) { - continue; + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id->override_library->hierarchy_root) || + id->override_library->hierarchy_root->lib != id->lib) { + CLOG_ERROR( + &LOG, + "Existing override hierarchy root ('%s') for ID '%s' is invalid, will try to find a " + "new valid one", + id->override_library->hierarchy_root != NULL ? + id->override_library->hierarchy_root->name : + "<NONE>", + id->name); + id->override_library->hierarchy_root = NULL; + } + else { + continue; + } } BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1402,7 +1416,7 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } - lib_override_root_hierarchy_set(bmain, id_root, id_root, NULL); + lib_override_root_hierarchy_set(bmain, id_root, id, NULL); BLI_assert(id->override_library->hierarchy_root != NULL); } @@ -1451,6 +1465,7 @@ static void lib_override_library_remap(Main *bmain, remapper, ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); BKE_id_remapper_free(remapper); + BLI_linklist_free(nomain_ids, NULL); } static bool lib_override_library_resync(Main *bmain, @@ -2058,6 +2073,10 @@ static bool lib_override_resync_tagging_finalize_recurse( CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name); + if (id_root->override_library == NULL) { + BLI_assert(0); + } + LinkNodePair **id_resync_roots_p; if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) { *id_resync_roots_p = MEM_callocN(sizeof(**id_resync_roots_p), __func__); diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 03e03dacfbc..b9ed783fa8c 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -502,13 +502,13 @@ BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img) } if (img) { - const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(img->x, img->y); + data = MEM_mallocN(data_size, __func__); IMB_rect_from_float(img); /* Just in case... */ data->width = img->x; data->height = img->y; - memcpy(data->rect, img->rect, sz - sizeof(*data)); + memcpy(data->rect, img->rect, data_size - sizeof(*data)); } if (bmain) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 002b496393f..1fbe4f768ff 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1123,15 +1123,16 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap BLI_ghash_free(gh_mat_map, NULL, NULL); } -void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval) +void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, const ID *data_eval) { ID *data_orig = ob_orig->data; short *orig_totcol = BKE_id_material_len_p(data_orig); Material ***orig_mat = BKE_id_material_array_p(data_orig); - short *eval_totcol = BKE_id_material_len_p(data_eval); - Material ***eval_mat = BKE_id_material_array_p(data_eval); + /* Can cast away const, because the data is not changed. */ + const short *eval_totcol = BKE_id_material_len_p((ID *)data_eval); + Material ***eval_mat = BKE_id_material_array_p((ID *)data_eval); if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) { return; diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc new file mode 100644 index 00000000000..adaf378ed27 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_task.hh" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BLI_memarena.h" + +#include "BLI_strict_flags.h" + +using namespace blender; + +enum { + CMP_CLOSE = 0, + CMP_EQUAL = 1, + CMP_APART = 2, +}; + +static int compare_v2_classify(const float uv_a[2], const float uv_b[2]) +{ + if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) { + return CMP_EQUAL; + } + /* Note that the ULP value is the primary value used to compare relative values + * as the absolute value doesn't account for float precision at difference scales. + * - For subdivision-surface ULP of 3 is sufficient, + * although this value is extremely small. + * - For bevel the URL of 12 is sufficient to merge UV's that appear to be connected + * with bevel on Suzanne beveled 15% with 6 segments. + * + * These values could be tweaked but should be kept on the small side to prevent + * unintentional joining of intentionally dis-connected UV's. + * + * Before v2.91 the threshold was either (`1e-4` or `0.05 / image_size` for selection picking). + * So picking used a threshold of `1e-4` for a 500x500 image and `1e-5` for a 5000x5000 image. + * Given this value worked reasonably well for a long time, the absolute difference should + * never exceed `1e-4` (#STD_UV_CONNECT_LIMIT which is still used in a few areas). */ + const float diff_abs = 1e-12f; + const int diff_ulp = 12; + + if (compare_ff_relative(uv_a[0], uv_b[0], diff_abs, diff_ulp) && + compare_ff_relative(uv_a[1], uv_b[1], diff_abs, diff_ulp)) { + return CMP_CLOSE; + } + return CMP_APART; +} + +static void merge_uvs_for_vertex(const Span<int> loops_for_vert, Span<MLoopUV *> mloopuv_layers) +{ + if (loops_for_vert.size() <= 1) { + return; + } + /* Manipulate a copy of the loop indices, de-duplicating UV's per layer. */ + Vector<int, 32> loops_merge; + loops_merge.reserve(loops_for_vert.size()); + for (MLoopUV *mloopuv : mloopuv_layers) { + BLI_assert(loops_merge.is_empty()); + loops_merge.extend_unchecked(loops_for_vert); + while (loops_merge.size() > 1) { + uint i_last = (uint)loops_merge.size() - 1; + const float *uv_src = mloopuv[loops_merge[0]].uv; + for (uint i = 1; i <= i_last;) { + float *uv_dst = mloopuv[loops_merge[i]].uv; + switch (compare_v2_classify(uv_src, uv_dst)) { + case CMP_CLOSE: { + uv_dst[0] = uv_src[0]; + uv_dst[1] = uv_src[1]; + ATTR_FALLTHROUGH; + } + case CMP_EQUAL: { + loops_merge[i] = loops_merge[i_last--]; + break; + } + case CMP_APART: { + /* Doesn't match, check the next UV. */ + i++; + break; + } + default: { + BLI_assert_unreachable(); + } + } + } + /* Finished de-duplicating with the first index, throw it away. */ + loops_merge[0] = loops_merge[i_last]; + loops_merge.resize(i_last); + } + loops_merge.clear(); + } +} + +void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me) +{ + if (me->totloop == 0) { + return; + } + const int mloopuv_layers_num = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + if (mloopuv_layers_num == 0) { + return; + } + + int *vert_map_mem; + struct MeshElemMap *vert_to_loop; + BKE_mesh_vert_loop_map_create( + &vert_to_loop, &vert_map_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); + + Vector<MLoopUV *> mloopuv_layers; + mloopuv_layers.reserve(mloopuv_layers_num); + for (int a = 0; a < mloopuv_layers_num; a++) { + MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a)); + mloopuv_layers.append_unchecked(mloopuv); + } + + Span<MLoopUV *> mloopuv_layers_as_span = mloopuv_layers.as_span(); + threading::parallel_for(IndexRange(me->totvert), 1024, [&](IndexRange range) { + for (const int64_t v_index : range) { + MeshElemMap &loops_for_vert = vert_to_loop[v_index]; + Span<int> loops_for_vert_span(loops_for_vert.indices, loops_for_vert.count); + merge_uvs_for_vertex(loops_for_vert_span, mloopuv_layers_as_span); + } + }); + + MEM_freeN(vert_to_loop); + MEM_freeN(vert_map_mem); +} diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 200eefb73ec..ffa39f79e7c 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4502,6 +4502,8 @@ static void registerCompositNodes() register_node_type_cmp_premulkey(); register_node_type_cmp_separate_xyz(); register_node_type_cmp_combine_xyz(); + register_node_type_cmp_separate_color(); + register_node_type_cmp_combine_color(); register_node_type_cmp_diff_matte(); register_node_type_cmp_distance_matte(); @@ -4574,6 +4576,8 @@ static void registerShaderNodes() register_node_type_sh_vect_transform(); register_node_type_sh_squeeze(); register_node_type_sh_invert(); + register_node_type_sh_sepcolor(); + register_node_type_sh_combcolor(); register_node_type_sh_seprgb(); register_node_type_sh_combrgb(); register_node_type_sh_sephsv(); @@ -4660,6 +4664,8 @@ static void registerTextureNodes() register_node_type_tex_distance(); register_node_type_tex_compose(); register_node_type_tex_decompose(); + register_node_type_tex_combine_color(); + register_node_type_tex_separate_color(); register_node_type_tex_output(); register_node_type_tex_viewer(); @@ -4821,6 +4827,7 @@ static void registerFunctionNodes() { register_node_type_fn_align_euler_to_vector(); register_node_type_fn_boolean_math(); + register_node_type_fn_combine_color(); register_node_type_fn_compare(); register_node_type_fn_float_to_int(); register_node_type_fn_input_bool(); @@ -4832,6 +4839,7 @@ static void registerFunctionNodes() register_node_type_fn_random_value(); register_node_type_fn_replace_string(); register_node_type_fn_rotate_euler(); + register_node_type_fn_separate_color(); register_node_type_fn_slice_string(); register_node_type_fn_string_length(); register_node_type_fn_value_to_string(); diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 2a25d73ed87..7f5146f14e0 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -2114,7 +2114,7 @@ static const char *get_obdata_defname(int type) case OB_SPEAKER: return DATA_("Speaker"); case OB_CURVES: - return DATA_("HairCurves"); + return DATA_("Curves"); case OB_POINTCLOUD: return DATA_("PointCloud"); case OB_VOLUME: diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index 31e1c17184f..0fed7e06b4a 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -71,7 +71,7 @@ static void extract_barycentric_pixels(UDIMTilePixels &tile_data, int x; for (x = minx; x < maxx; x++) { - float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y); + float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y); float3 barycentric_weights; barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 34720bf7d6b..353f89068d8 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -833,8 +833,8 @@ static void ptcache_rigidbody_interpolate(int index, memcpy(orn, data + 3, sizeof(float[4])); } else { - PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, index, pos); - PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, index, orn); + PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, pos); + PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, 0, orn); } const float t = (cfra - cfra1) / (cfra2 - cfra1); diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index eed187b1b0a..e203d32a658 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -274,7 +274,7 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int scene_dst->nodetree, (void *)(&scene_src->id), &scene_dst->id, - ID_REMAP_SKIP_NEVER_NULL_USAGE); + ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_SKIP_USER_CLEAR); } if (scene_src->rigidbody_world) { diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 7704a74841a..e8c7aff75d1 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -116,15 +116,15 @@ void Spline::reverse() this->mark_cache_invalid(); } -int Spline::evaluated_edges_size() const +int Spline::evaluated_edges_num() const { - const int eval_size = this->evaluated_points_size(); - if (eval_size < 2) { + const int eval_num = this->evaluated_points_num(); + if (eval_num < 2) { /* Two points are required for an edge. */ return 0; } - return this->is_cyclic_ ? eval_size : eval_size - 1; + return this->is_cyclic_ ? eval_num : eval_num - 1; } float Spline::length() const @@ -133,11 +133,11 @@ float Spline::length() const return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last(); } -int Spline::segments_size() const +int Spline::segments_num() const { - const int size = this->size(); + const int num = this->size(); - return is_cyclic_ ? size : size - 1; + return is_cyclic_ ? num : num - 1; } bool Spline::is_cyclic() const @@ -177,7 +177,7 @@ Span<float> Spline::evaluated_lengths() const return evaluated_lengths_cache_; } - const int total = evaluated_edges_size(); + const int total = evaluated_edges_num(); evaluated_lengths_cache_.resize(total); if (total != 0) { Span<float3> positions = this->evaluated_positions(); @@ -242,8 +242,8 @@ Span<float3> Spline::evaluated_tangents() const return evaluated_tangents_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_tangents_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_tangents_cache_.resize(eval_num); Span<float3> positions = this->evaluated_positions(); @@ -369,8 +369,8 @@ Span<float3> Spline::evaluated_normals() const return evaluated_normals_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_normals_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_normals_cache_.resize(eval_num); Span<float3> tangents = this->evaluated_tangents(); MutableSpan<float3> normals = evaluated_normals_cache_; @@ -410,7 +410,7 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); const int index = offset - lengths.begin(); - const int next_index = (index == this->evaluated_points_size() - 1) ? 0 : index + 1; + const int next_index = (index == this->evaluated_points_num() - 1) ? 0 : index + 1; const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; const float length_in_segment = length - previous_length; @@ -420,30 +420,30 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const return LookupResult{index, next_index, factor}; } -Array<float> Spline::sample_uniform_index_factors(const int samples_size) const +Array<float> Spline::sample_uniform_index_factors(const int samples_num) const { const Span<float> lengths = this->evaluated_lengths(); - BLI_assert(samples_size > 0); - Array<float> samples(samples_size); + BLI_assert(samples_num > 0); + Array<float> samples(samples_num); samples[0] = 0.0f; - if (samples_size == 1) { + if (samples_num == 1) { return samples; } const float total_length = this->length(); - const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1)); + const float sample_length = total_length / (samples_num - (is_cyclic_ ? 0 : 1)); /* Store the length at the previous evaluated point in a variable so it can * start out at zero (the lengths array doesn't contain 0 for the first point). */ float prev_length = 0.0f; int i_sample = 1; - for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) { + for (const int i_evaluated : IndexRange(this->evaluated_edges_num())) { const float length = lengths[i_evaluated]; /* Add every sample that fits in this evaluated edge. */ - while ((sample_length * i_sample) < length && i_sample < samples_size) { + while ((sample_length * i_sample) < length && i_sample < samples_num) { const float factor = (sample_length * i_sample - prev_length) / (length - prev_length); samples[i_sample] = i_evaluated + factor; i_sample++; @@ -454,8 +454,8 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const /* Zero lengths or float inaccuracies can cause invalid values, or simply * skip some, so set the values that weren't completed in the main loop. */ - for (const int i : IndexRange(i_sample, samples_size - i_sample)) { - samples[i] = float(samples_size); + for (const int i : IndexRange(i_sample, samples_num - i_sample)) { + samples[i] = float(samples_num); } if (!is_cyclic_) { @@ -468,23 +468,23 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const { - const int eval_size = this->evaluated_points_size(); + const int eval_num = this->evaluated_points_num(); if (is_cyclic_) { - if (index_factor < eval_size) { + if (index_factor < eval_num) { const int index = std::floor(index_factor); - const int next_index = (index < eval_size - 1) ? index + 1 : 0; + const int next_index = (index < eval_num - 1) ? index + 1 : 0; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 1, 0, 1.0f}; + return LookupResult{eval_num - 1, 0, 1.0f}; } - if (index_factor < eval_size - 1) { + if (index_factor < eval_num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 2, eval_size - 1, 1.0f}; + return LookupResult{eval_num - 2, eval_num - 1, 1.0f}; } void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const @@ -504,7 +504,7 @@ void Spline::sample_with_index_factors(const GVArray &src, Span<float> index_factors, GMutableSpan dst) const { - BLI_assert(src.size() == this->evaluated_points_size()); + BLI_assert(src.size() == this->evaluated_points_num()); blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 8e207f93bf5..80515d0ef37 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -335,7 +335,7 @@ void BezierSpline::mark_cache_invalid() auto_handles_dirty_ = true; } -int BezierSpline::evaluated_points_size() const +int BezierSpline::evaluated_points_num() const { BLI_assert(this->size() > 0); return this->control_point_offsets().last(); @@ -502,12 +502,12 @@ Span<float> BezierSpline::evaluated_mappings() const return evaluated_mapping_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_mapping_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_mapping_cache_.resize(eval_num); MutableSpan<float> mappings = evaluated_mapping_cache_; - if (eval_size == 1) { + if (eval_num == 1) { mappings.first() = 0.0f; mapping_cache_dirty_ = false; return mappings; @@ -517,7 +517,7 @@ Span<float> BezierSpline::evaluated_mappings() const blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings); + calculate_mappings_linear_resolution(offsets, num, resolution_, is_cyclic_, mappings); }); mapping_cache_dirty_ = false; @@ -535,15 +535,15 @@ Span<float3> BezierSpline::evaluated_positions() const return evaluated_position_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); MutableSpan<float3> positions = evaluated_position_cache_; - if (size == 1) { + if (num == 1) { /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ - BLI_assert(eval_size == 1); + BLI_assert(eval_num == 1); positions.first() = positions_.first(); position_cache_dirty_ = false; return positions; @@ -556,7 +556,7 @@ Span<float3> BezierSpline::evaluated_positions() const const int grain_size = std::max(512 / resolution_, 1); blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - blender::threading::parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { + blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) { for (const int i : range) { this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); } @@ -564,7 +564,7 @@ Span<float3> BezierSpline::evaluated_positions() const }); if (is_cyclic_) { this->evaluate_segment( - size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1])); + num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 1])); } else { /* Since evaluating the bezier segment doesn't add the final point, @@ -579,23 +579,23 @@ Span<float3> BezierSpline::evaluated_positions() const BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( const float index_factor) const { - const int size = this->size(); + const int num = this->size(); if (is_cyclic_) { - if (index_factor < size) { + if (index_factor < num) { const int index = std::floor(index_factor); - const int next_index = (index < size - 1) ? index + 1 : 0; + const int next_index = (index < num - 1) ? index + 1 : 0; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 1, 0, 1.0f}; + return InterpolationData{num - 1, 0, 1.0f}; } - if (index_factor < size - 1) { + if (index_factor < num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 2, size - 1, 1.0f}; + return InterpolationData{num - 2, num - 1, 1.0f}; } /* Use a spline argument to avoid adding this to the header. */ @@ -605,7 +605,7 @@ static void interpolate_to_evaluated_impl(const BezierSpline &spline, MutableSpan<T> dst) { BLI_assert(src.size() == spline.size()); - BLI_assert(dst.size() == spline.evaluated_points_size()); + BLI_assert(dst.size() == spline.evaluated_points_num()); Span<float> mappings = spline.evaluated_mappings(); for (const int i : dst.index_range()) { @@ -627,8 +627,8 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const return src; } - const int eval_size = this->evaluated_points_size(); - if (eval_size == 1) { + const int eval_num = this->evaluated_points_num(); + if (eval_num == 1) { return src; } @@ -636,7 +636,7 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(eval_size); + Array<T> values(eval_num); interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 9d1d5a53a43..a7eeb82d854 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -124,19 +124,19 @@ void NURBSpline::mark_cache_invalid() length_cache_dirty_ = true; } -int NURBSpline::evaluated_points_size() const +int NURBSpline::evaluated_points_num() const { - if (!this->check_valid_size_and_order()) { + if (!this->check_valid_num_and_order()) { return 0; } - return resolution_ * this->segments_size(); + return resolution_ * this->segments_num(); } void NURBSpline::correct_end_tangents() const { } -bool NURBSpline::check_valid_size_and_order() const +bool NURBSpline::check_valid_num_and_order() const { if (this->size() < order_) { return false; @@ -152,10 +152,10 @@ bool NURBSpline::check_valid_size_and_order() const return true; } -int NURBSpline::knots_size() const +int NURBSpline::knots_num() const { - const int size = this->size() + order_; - return is_cyclic_ ? size + order_ - 1 : size; + const int num = this->size() + order_; + return is_cyclic_ ? num + order_ - 1 : num; } void NURBSpline::calculate_knots() const @@ -173,7 +173,7 @@ void NURBSpline::calculate_knots() const * Covers both Cyclic and EndPoint cases. */ const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0); - knots_.resize(this->knots_size()); + knots_.resize(this->knots_num()); MutableSpan<float> knots = knots_; int r = head; @@ -203,13 +203,13 @@ void NURBSpline::calculate_knots() const Span<float> NURBSpline::knots() const { if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } std::lock_guard lock{knots_mutex_}; if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } @@ -221,7 +221,7 @@ Span<float> NURBSpline::knots() const } static void calculate_basis_for_point(const float parameter, - const int size, + const int num, const int degree, const Span<float> knots, MutableSpan<float> r_weights, @@ -231,7 +231,7 @@ static void calculate_basis_for_point(const float parameter, int start = 0; int end = 0; - for (const int i : IndexRange(size + degree)) { + for (const int i : IndexRange(num + degree)) { const bool knots_equal = knots[i] == knots[i + 1]; if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) { continue; @@ -248,7 +248,7 @@ static void calculate_basis_for_point(const float parameter, for (const int i_order : IndexRange(2, degree)) { if (end + i_order >= knots.size()) { - end = size + degree - i_order; + end = num + degree - i_order; } for (const int i : IndexRange(end - start + 1)) { const int knot_index = start + i; @@ -284,16 +284,16 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const return basis_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); const int order = this->order(); const int degree = order - 1; - basis_cache_.weights.resize(eval_size * order); - basis_cache_.start_indices.resize(eval_size); + basis_cache_.weights.resize(eval_num * order); + basis_cache_.start_indices.resize(eval_num); - if (eval_size == 0) { + if (eval_num == 0) { return basis_cache_; } @@ -303,14 +303,14 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const const Span<float> control_weights = this->weights(); const Span<float> knots = this->knots(); - const int last_control_point_index = is_cyclic_ ? size + degree : size; + const int last_control_point_index = is_cyclic_ ? num + degree : num; const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / this->evaluated_edges_size(); - for (const int i : IndexRange(eval_size)) { + const float step = (end - start) / this->evaluated_edges_num(); + for (const int i : IndexRange(eval_num)) { /* Clamp parameter due to floating point inaccuracy. */ - const float parameter = std::clamp(start + step * i, knots[0], knots[size + degree]); + const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]); MutableSpan<float> point_weights = basis_weights.slice(i * order, order); @@ -318,7 +318,7 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); for (const int j : point_weights.index_range()) { - const int point_index = (basis_start_indices[i] + j) % size; + const int point_index = (basis_start_indices[i] + j) % num; point_weights[j] *= control_weights[point_index]; } } @@ -333,7 +333,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const blender::VArray<T> &src, MutableSpan<T> dst) { - const int size = src.size(); + const int num = src.size(); blender::attribute_math::DefaultMixer<T> mixer(dst); for (const int i : dst.index_range()) { @@ -341,7 +341,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const int start_index = basis_cache.start_indices[i]; for (const int j : point_weights.index_range()) { - const int point_index = (start_index + j) % size; + const int point_index = (start_index + j) % num; mixer.mix_in(i, src[point_index], point_weights[j]); } } @@ -363,7 +363,7 @@ GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(this->evaluated_points_size()); + Array<T> values(this->evaluated_points_num()); interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } @@ -383,8 +383,8 @@ Span<float3> NURBSpline::evaluated_positions() const return evaluated_position_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); /* TODO: Avoid copying the evaluated data from the temporary array. */ VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span()); diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 122f7f6c059..c3cc268c81c 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -76,7 +76,7 @@ void PolySpline::mark_cache_invalid() length_cache_dirty_ = true; } -int PolySpline::evaluated_points_size() const +int PolySpline::evaluated_points_num() const { return this->size(); } diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c index 1c5078df1f3..12a5f00a68b 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -295,16 +295,16 @@ static void init_functions(OpenSubdiv_Converter *converter) static void initialize_manifold_index_array(const BLI_bitmap *used_map, const int num_elements, - int **indices_r, - int **indices_reverse_r, - int *num_manifold_elements_r) + int **r_indices, + int **r_indices_reverse, + int *r_num_manifold_elements) { int *indices = NULL; - if (indices_r != NULL) { + if (r_indices != NULL) { indices = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices"); } int *indices_reverse = NULL; - if (indices_reverse_r != NULL) { + if (r_indices_reverse != NULL) { indices_reverse = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices reverse"); } int offset = 0; @@ -324,13 +324,13 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map, offset++; } } - if (indices_r != NULL) { - *indices_r = indices; + if (r_indices != NULL) { + *r_indices = indices; } - if (indices_reverse_r != NULL) { - *indices_reverse_r = indices_reverse; + if (r_indices_reverse != NULL) { + *r_indices_reverse = indices_reverse; } - *num_manifold_elements_r = num_elements - offset; + *r_num_manifold_elements = num_elements - offset; } static void initialize_manifold_indices(ConverterStorage *storage) diff --git a/source/blender/blenkernel/intern/tracking_plane_tracker.c b/source/blender/blenkernel/intern/tracking_plane_tracker.c index 5e60f6f59a9..c4379ea61bc 100644 --- a/source/blender/blenkernel/intern/tracking_plane_tracker.c +++ b/source/blender/blenkernel/intern/tracking_plane_tracker.c @@ -21,12 +21,12 @@ typedef double Vec2[2]; static int point_markers_correspondences_on_both_image( - MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **x1_r, Vec2 **x2_r) + MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **r_x1, Vec2 **r_x2) { Vec2 *x1, *x2; - *x1_r = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1"); - *x2_r = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2"); + *r_x1 = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1"); + *r_x2 = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2"); int correspondence_index = 0; for (int i = 0; i < plane_track->point_tracksnr; i++) { diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 3ce2b90e729..9a24b09fc66 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -178,7 +178,7 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries); * Convert given entry's size into human-readable strings. */ void BLI_filelist_entry_size_to_string(const struct stat *st, - uint64_t sz, + uint64_t st_size_fallback, bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN]); /** diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh new file mode 100644 index 00000000000..62478556d9b --- /dev/null +++ b/source/blender/blenlib/BLI_float3x3.hh @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cmath> +#include <cstdint> + +#include "BLI_assert.h" +#include "BLI_math_base.h" +#include "BLI_math_matrix.h" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.h" + +namespace blender { + +struct float3x3 { + /* A 3x3 matrix in column major order. */ + float values[3][3]; + + float3x3() = default; + + float3x3(const float *matrix) + { + memcpy(values, matrix, sizeof(float) * 3 * 3); + } + + float3x3(const float matrix[3][3]) : float3x3(static_cast<const float *>(matrix[0])) + { + } + + static float3x3 zero() + { + float3x3 result; + zero_m3(result.values); + return result; + } + + static float3x3 identity() + { + float3x3 result; + unit_m3(result.values); + return result; + } + + static float3x3 from_translation(const float2 translation) + { + float3x3 result = identity(); + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + return result; + } + + static float3x3 from_rotation(float rotation) + { + float3x3 result = zero(); + const float cosine = std::cos(rotation); + const float sine = std::sin(rotation); + result.values[0][0] = cosine; + result.values[0][1] = sine; + result.values[1][0] = -sine; + result.values[1][1] = cosine; + result.values[2][2] = 1.0f; + return result; + } + + static float3x3 from_translation_rotation_scale(const float2 translation, + float rotation, + const float2 scale) + { + float3x3 result; + const float cosine = std::cos(rotation); + const float sine = std::sin(rotation); + result.values[0][0] = scale.x * cosine; + result.values[0][1] = scale.x * sine; + result.values[0][2] = 0.0f; + result.values[1][0] = scale.y * -sine; + result.values[1][1] = scale.y * cosine; + result.values[1][2] = 0.0f; + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + result.values[2][2] = 1.0f; + return result; + } + + static float3x3 from_normalized_axes(const float2 translation, + const float2 horizontal, + const float2 vertical) + { + BLI_ASSERT_UNIT_V2(horizontal); + BLI_ASSERT_UNIT_V2(vertical); + + float3x3 result; + result.values[0][0] = horizontal.x; + result.values[0][1] = horizontal.y; + result.values[0][2] = 0.0f; + result.values[1][0] = vertical.x; + result.values[1][1] = vertical.y; + result.values[1][2] = 0.0f; + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + result.values[2][2] = 1.0f; + return result; + } + + /* Construct a transformation that is pivoted around the given origin point. So for instance, + * from_origin_transformation(from_rotation(M_PI_2), float2(0.0f, 2.0f)) + * will construct a transformation representing a 90 degree rotation around the point (0, 2). */ + static float3x3 from_origin_transformation(const float3x3 &transformation, const float2 origin) + { + return from_translation(origin) * transformation * from_translation(-origin); + } + + operator float *() + { + return &values[0][0]; + } + + operator const float *() const + { + return &values[0][0]; + } + + float *operator[](const int64_t index) + { + BLI_assert(index >= 0); + BLI_assert(index < 3); + return &values[index][0]; + } + + const float *operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < 3); + return &values[index][0]; + } + + using c_style_float3x3 = float[3][3]; + c_style_float3x3 &ptr() + { + return values; + } + + const c_style_float3x3 &ptr() const + { + return values; + } + + friend float3x3 operator*(const float3x3 &a, const float3x3 &b) + { + float3x3 result; + mul_m3_m3m3(result.values, a.values, b.values); + return result; + } + + void operator*=(const float3x3 &other) + { + mul_m3_m3_post(values, other.values); + } + + friend float2 operator*(const float3x3 &transformation, const float2 &vector) + { + float2 result; + mul_v2_m3v2(result, transformation.values, vector); + return result; + } + + friend float2 operator*(const float3x3 &transformation, const float (*vector)[2]) + { + return transformation * float2(vector); + } + + float3x3 transposed() const + { + float3x3 result; + transpose_m3_m3(result.values, values); + return result; + } + + float3x3 inverted() const + { + float3x3 result; + invert_m3_m3(result.values, values); + return result; + } + + friend bool operator==(const float3x3 &a, const float3x3 &b) + { + return equals_m3m3(a.values, b.values); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh index 6fe1c6513a2..f13641c3a65 100644 --- a/source/blender/blenlib/BLI_length_parameterize.hh +++ b/source/blender/blenlib/BLI_length_parameterize.hh @@ -17,7 +17,7 @@ namespace blender::length_parameterize { * Return the size of the necessary lengths array for a group of points, taking into account the * possible last cyclic segment. * - * \note This is the same as #bke::curves::curve_segment_size. + * \note This is the same as #bke::curves::curve_segment_num. */ inline int lengths_num(const int points_num, const bool cyclic) { diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 45abac33795..0344622e81d 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -343,8 +343,16 @@ int BLI_strcmp_ignore_pad(const char *str1, const char *str2, char pad) ATTR_WAR */ size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/** + * String case conversion, not affected by locale. + */ + void BLI_str_tolower_ascii(char *str, size_t len) ATTR_NONNULL(); void BLI_str_toupper_ascii(char *str, size_t len) ATTR_NONNULL(); + +char BLI_tolower_ascii(const char c); +char BLI_toupper_ascii(const char c); + /** * Strip white-space from end of the string. */ diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index d87a86ce696..904dea66f7a 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -77,17 +77,19 @@ Value parallel_reduce(IndexRange range, const Reduction &reduction) { #ifdef WITH_TBB - return tbb::parallel_reduce( - tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), - identity, - [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { - return function(IndexRange(subrange.begin(), subrange.size()), ident); - }, - reduction); + if (range.size() >= grain_size) { + return tbb::parallel_reduce( + tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), + identity, + [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { + return function(IndexRange(subrange.begin(), subrange.size()), ident); + }, + reduction); + } #else UNUSED_VARS(grain_size, reduction); - return function(range, identity); #endif + return function(range, identity); } /** diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e0f28522d6c..109230ebfa7 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -79,8 +79,8 @@ set(SRC intern/kdtree_3d.c intern/kdtree_4d.c intern/lasso_2d.c - intern/listbase.c intern/length_parameterize.cc + intern/listbase.c intern/math_base.c intern/math_base_inline.c intern/math_base_safe_inline.c @@ -199,6 +199,7 @@ set(SRC BLI_fileops.hh BLI_fileops_types.h BLI_filereader.h + BLI_float3x3.hh BLI_float4x4.hh BLI_fnmatch.h BLI_function_ref.hh @@ -274,8 +275,8 @@ set(SRC BLI_multi_value_map.hh BLI_noise.h BLI_noise.hh - BLI_path_util.h BLI_parameter_pack_utils.hh + BLI_path_util.h BLI_polyfill_2d.h BLI_polyfill_2d_beautify.h BLI_probing_strategies.hh @@ -431,6 +432,7 @@ if(WITH_GTESTS) tests/BLI_edgehash_test.cc tests/BLI_expr_pylike_eval_test.cc tests/BLI_fileops_test.cc + tests/BLI_float3x3_test.cc tests/BLI_function_ref_test.cc tests/BLI_generic_array_test.cc tests/BLI_generic_span_test.cc diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index 76fc5b6342a..c6178ebb3a0 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -237,7 +237,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_ } void BLI_filelist_entry_size_to_string(const struct stat *st, - const uint64_t sz, + const uint64_t st_size_fallback, /* Used to change MB -> M, etc. - is that really useful? */ const bool UNUSED(compact), char r_size[FILELIST_DIRENTRY_SIZE_LEN]) @@ -247,7 +247,7 @@ void BLI_filelist_entry_size_to_string(const struct stat *st, * will buy us some time until files get bigger than 4GB or until * everyone starts using __USE_FILE_OFFSET64 or equivalent. */ - double size = (double)(st ? st->st_size : sz); + double size = (double)(st ? st->st_size : st_size_fallback); #ifdef WIN32 BLI_str_format_byte_unit(r_size, size, false); #else diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index aeeaf47d813..07e9eaf0f42 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -238,7 +238,7 @@ void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l) { const float cmax = max_fff(r, g, b); const float cmin = min_fff(r, g, b); - float h, s, l = min_ff(1.0, (cmax + cmin) / 2.0f); + float h, s, l = min_ff(1.0f, (cmax + cmin) / 2.0f); if (cmax == cmin) { h = s = 0.0f; /* achromatic */ diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 74559751d91..8387eb5f4f9 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -914,14 +914,22 @@ size_t BLI_strnlen(const char *s, const size_t maxlen) /** \name String Case Conversion * \{ */ +char BLI_tolower_ascii(const char c) +{ + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char BLI_toupper_ascii(const char c) +{ + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + void BLI_str_tolower_ascii(char *str, const size_t len) { size_t i; for (i = 0; (i < len) && str[i]; i++) { - if (str[i] >= 'A' && str[i] <= 'Z') { - str[i] += 'a' - 'A'; - } + str[i] = BLI_tolower_ascii(str[i]); } } @@ -930,9 +938,7 @@ void BLI_str_toupper_ascii(char *str, const size_t len) size_t i; for (i = 0; (i < len) && str[i]; i++) { - if (str[i] >= 'a' && str[i] <= 'z') { - str[i] -= 'a' - 'A'; - } + str[i] = BLI_toupper_ascii(str[i]); } } diff --git a/source/blender/blenlib/tests/BLI_float3x3_test.cc b/source/blender/blenlib/tests/BLI_float3x3_test.cc new file mode 100644 index 00000000000..d22993ee69e --- /dev/null +++ b/source/blender/blenlib/tests/BLI_float3x3_test.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "BLI_float3x3.hh" +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + +namespace blender::tests { + +TEST(float3x3, Identity) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::identity(); + float2 result = transformation * point; + EXPECT_EQ(result, point); +} + +TEST(float3x3, Translation) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 6.0f); + EXPECT_FLOAT_EQ(result[1], 5.0f); +} + +TEST(float3x3, Rotation) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_rotation(M_PI_2); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -2.0f); + EXPECT_FLOAT_EQ(result[1], 1.0f); +} + +TEST(float3x3, TranslationRotationScale) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_2, float2(2.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -5.0f); + EXPECT_FLOAT_EQ(result[1], 5.0f); +} + +TEST(float3x3, NormalizedAxes) +{ + float2 point(1.0f, 2.0f); + + /* The horizontal is aligned with (1, 1) and vertical is aligned with (-1, 1), in other words, a + * Pi / 4 rotation. */ + float value = std::sqrt(2.0f) / 2.0f; + float3x3 transformation = float3x3::from_normalized_axes( + float2(1.0f, 3.0f), float2(value), float2(-value, value)); + float2 result = transformation * point; + + float3x3 expected_transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_4, float2(1.0f)); + float2 expected = expected_transformation * point; + + EXPECT_FLOAT_EQ(result[0], expected[0]); + EXPECT_FLOAT_EQ(result[1], expected[1]); +} + +TEST(float3x3, PostTransformationMultiplication) +{ + float2 point(1.0f, 2.0f); + float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f)); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = translation * rotation; + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 3.0f); + EXPECT_FLOAT_EQ(result[1], 4.0f); +} + +TEST(float3x3, PreTransformationMultiplication) +{ + float2 point(1.0f, 2.0f); + float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f)); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = rotation * translation; + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -5.0f); + EXPECT_FLOAT_EQ(result[1], 6.0f); +} + +TEST(float3x3, TransformationMultiplicationAssignment) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f)); + transformation *= float3x3::from_rotation(M_PI_2); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 3.0f); + EXPECT_FLOAT_EQ(result[1], 4.0f); +} + +TEST(float3x3, Inverted) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_4, float2(1.0f)); + transformation *= transformation.inverted(); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 1.0f); + EXPECT_FLOAT_EQ(result[1], 2.0f); +} + +TEST(float3x3, Origin) +{ + float2 point(1.0f, 2.0f); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = float3x3::from_origin_transformation(rotation, float2(0.0f, 2.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 0.0f); + EXPECT_FLOAT_EQ(result[1], 3.0f); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index bfd297214c0..4f6f4a5c413 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -663,7 +663,7 @@ TEST(path_util, PathContains) EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside")) << "A path contains its subdirectory"; EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside")) << "Windows paths should be supported as well"; @@ -672,7 +672,7 @@ TEST(path_util, PathContains) EXPECT_FALSE(BLI_path_contains("/some/path", "/")) << "Root directory not be contained in a subdirectory"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library")) << "Just sharing a suffix is not enough, path semantics should be followed"; EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) diff --git a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc index 0ff488202c2..09bb1e7239f 100644 --- a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc +++ b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc @@ -57,23 +57,23 @@ static void str_ghash_tests(GHash *ghash, const char *id) printf("\n========== STARTING %s ==========\n", id); #ifdef TEXT_CORPUS_PATH - size_t sz = 0; + size_t data_size = 0; char *data; { struct stat st; if (stat(TEXT_CORPUS_PATH, &st) == 0) - sz = st.st_size; + data_size = st.st_size; } - if (sz != 0) { + if (data_size != 0) { FILE *f = fopen(TEXT_CORPUS_PATH, "r"); - data = (char *)MEM_mallocN(sz + 1, __func__); - if (fread(data, sizeof(*data), sz, f) != sz) { + data = (char *)MEM_mallocN(data_size + 1, __func__); + if (fread(data, sizeof(*data), data_size, f) != data_size) { printf("ERROR in reading file %s!", TEXT_CORPUS_PATH); MEM_freeN(data); data = BLI_strdup(words10k); } - data[sz] = '\0'; + data[data_size] = '\0'; fclose(f); } else { diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 27890a908ab..16bad11629a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1477,14 +1477,14 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) const int width = fd_data[0]; const int height = fd_data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + data = MEM_mallocN(data_size, __func__); if (data) { - BLI_assert((sz - sizeof(*data)) == + BLI_assert((data_size - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2))); data->width = width; data->height = height; - memcpy(data->rect, &fd_data[2], sz - sizeof(*data)); + memcpy(data->rect, &fd_data[2], data_size - sizeof(*data)); } } } @@ -2068,7 +2068,7 @@ static void direct_link_id_embedded_id(BlendDataReader *reader, static int direct_link_id_restore_recalc_exceptions(const ID *id_current) { /* Exception for armature objects, where the pose has direct points to the - * armature databolock. */ + * armature data-block. */ if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) { return ID_RECALC_GEOMETRY; } @@ -3857,14 +3857,14 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) const int width = data[0]; const int height = data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - bfd->main->blen_thumb = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + bfd->main->blen_thumb = MEM_mallocN(data_size, __func__); - BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) == + BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2))); bfd->main->blen_thumb->width = width; bfd->main->blen_thumb->height = height; - memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb)); + memcpy(bfd->main->blen_thumb->rect, &data[2], data_size - sizeof(*bfd->main->blen_thumb)); } } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 57c8411dfe7..43cba8e6d00 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2735,6 +2735,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } FOREACH_NODETREE_END; + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name( + ntree, GEO_NODE_SUBDIVISION_SURFACE, "Crease", "Edge Crease"); + } + } } if (!MAIN_VERSION_ATLEAST(bmain, 302, 13)) { @@ -2778,5 +2785,260 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) image->seamfix_distance = 2.5f; } } + + /* Replace legacy combine/separate color nodes */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + /* In geometry nodes, replace shader combine/separate color nodes with function nodes */ + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color"); + + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case SH_NODE_COMBRGB_LEGACY: { + node->type = FN_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "FunctionNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPRGB_LEGACY: { + node->type = FN_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "FunctionNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + + /* In compositing nodes, replace combine/separate RGBA/HSVA/YCbCrA/YCCA nodes with + * combine/separate color */ + if (ntree->type == NTREE_COMPOSIT) { + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "H", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "S", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "V", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Y", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cb", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cr", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "Y", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "U", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "V", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "H", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "S", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "V", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Y", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cb", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cr", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "Y", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "U", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "V", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "A", "Alpha"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case CMP_NODE_COMBRGBA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBHSVA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBYCCA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YCC; + storage->ycc_mode = node->custom1; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBYUVA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YUV; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPRGBA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPHSVA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPYCCA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YCC; + storage->ycc_mode = node->custom1; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPYUVA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YUV; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + + /* In texture nodes, replace combine/separate RGBA with combine/separate color */ + if (ntree->type == NTREE_TEXTURE) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case TEX_NODE_COMPOSE_LEGACY: { + node->type = TEX_NODE_COMBINE_COLOR; + node->custom1 = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "TextureNodeCombineColor"); + break; + } + case TEX_NODE_DECOMPOSE_LEGACY: { + node->type = TEX_NODE_SEPARATE_COLOR; + node->custom1 = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "TextureNodeSeparateColor"); + break; + } + } + } + } + + /* In shader nodes, replace combine/separate RGB/HSV with combine/separate color */ + if (ntree->type == NTREE_SHADER) { + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color"); + + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "H", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "S", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "V", "Blue"); + + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color"); + + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "H", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "S", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "V", "Blue"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case SH_NODE_COMBRGB_LEGACY: { + node->type = SH_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "ShaderNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_COMBHSV_LEGACY: { + node->type = SH_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "ShaderNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPRGB_LEGACY: { + node->type = SH_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "ShaderNodeSeparateColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPHSV_LEGACY: { + node->type = SH_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "ShaderNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + } + FOREACH_NODETREE_END; + + /* Initialize brush curves sculpt settings. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->ob_mode != OB_MODE_SCULPT_CURVES) { + continue; + } + if (brush->curves_sculpt_settings->points_per_curve == 0) { + brush->curves_sculpt_settings->points_per_curve = 8; + } + } } } diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 0246850123a..9d5737a5b71 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -265,8 +265,20 @@ typedef struct BMFace { * (the length of #BMFace.l_first circular linked list). */ int len; - float no[3]; /* face normal */ - short mat_nr; /* material index */ + /** + * Face normal, see #BM_face_calc_normal. + */ + float no[3]; + /** + * Material index, typically >= 0 and < #Mesh.totcol although this isn't enforced + * Python for e.g. can set this to any positive value since scripts may create + * mesh data first and setup material slots later. + * + * When using to index into a material array it's range should be checked first, + * values exceeding the range should be ignored or treated as zero + * (if a material slot needs to be used - when drawing for e.g.) + */ + short mat_nr; // short _pad[3]; } BMFace; diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 66f813e02b2..55e349423bb 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -259,12 +259,16 @@ set(SRC # converter nodes nodes/COM_CombineColorNode.cc nodes/COM_CombineColorNode.h + nodes/COM_CombineColorNodeLegacy.cc + nodes/COM_CombineColorNodeLegacy.h nodes/COM_CombineXYZNode.cc nodes/COM_CombineXYZNode.h nodes/COM_IDMaskNode.cc nodes/COM_IDMaskNode.h nodes/COM_SeparateColorNode.cc nodes/COM_SeparateColorNode.h + nodes/COM_SeparateColorNodeLegacy.cc + nodes/COM_SeparateColorNodeLegacy.h nodes/COM_SeparateXYZNode.cc nodes/COM_SeparateXYZNode.h diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 13d412a338b..6d7341376e9 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -29,6 +29,7 @@ #include "COM_ColorSpillNode.h" #include "COM_ColorToBWNode.h" #include "COM_CombineColorNode.h" +#include "COM_CombineColorNodeLegacy.h" #include "COM_CombineXYZNode.h" #include "COM_CompositorNode.h" #include "COM_ConvertAlphaNode.h" @@ -82,6 +83,7 @@ #include "COM_ScaleOperation.h" #include "COM_SceneTimeNode.h" #include "COM_SeparateColorNode.h" +#include "COM_SeparateColorNodeLegacy.h" #include "COM_SeparateXYZNode.h" #include "COM_SetAlphaNode.h" #include "COM_SetValueOperation.h" @@ -169,28 +171,34 @@ Node *COM_convert_bnode(bNode *b_node) case CMP_NODE_BRIGHTCONTRAST: node = new BrightnessNode(b_node); break; - case CMP_NODE_SEPRGBA: + case CMP_NODE_SEPARATE_COLOR: + node = new SeparateColorNode(b_node); + break; + case CMP_NODE_COMBINE_COLOR: + node = new CombineColorNode(b_node); + break; + case CMP_NODE_SEPRGBA_LEGACY: node = new SeparateRGBANode(b_node); break; - case CMP_NODE_COMBRGBA: + case CMP_NODE_COMBRGBA_LEGACY: node = new CombineRGBANode(b_node); break; - case CMP_NODE_SEPHSVA: + case CMP_NODE_SEPHSVA_LEGACY: node = new SeparateHSVANode(b_node); break; - case CMP_NODE_COMBHSVA: + case CMP_NODE_COMBHSVA_LEGACY: node = new CombineHSVANode(b_node); break; - case CMP_NODE_SEPYUVA: + case CMP_NODE_SEPYUVA_LEGACY: node = new SeparateYUVANode(b_node); break; - case CMP_NODE_COMBYUVA: + case CMP_NODE_COMBYUVA_LEGACY: node = new CombineYUVANode(b_node); break; - case CMP_NODE_SEPYCCA: + case CMP_NODE_SEPYCCA_LEGACY: node = new SeparateYCCANode(b_node); break; - case CMP_NODE_COMBYCCA: + case CMP_NODE_COMBYCCA_LEGACY: node = new CombineYCCANode(b_node); break; case CMP_NODE_ALPHAOVER: diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index 0c8748fd2d6..ca2c59478fd 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -12,7 +12,7 @@ CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node) } void CombineColorNode::convert_to_operations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext &UNUSED(context)) const { NodeInput *input_rsocket = this->get_input_socket(0); NodeInput *input_gsocket = this->get_input_socket(1); @@ -40,7 +40,39 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); converter.map_input_socket(input_asocket, operation->get_input_socket(3)); - NodeOperation *color_conv = get_color_converter(context); + bNode *editor_node = this->get_bnode(); + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + + NodeOperation *color_conv = nullptr; + switch (storage->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case CMP_NODE_COMBSEP_COLOR_HSV: { + color_conv = new ConvertHSVToRGBOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_HSL: { + color_conv = new ConvertHSLToRGBOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_YCC: { + ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); + operation->set_mode(storage->ycc_mode); + color_conv = operation; + break; + } + case CMP_NODE_COMBSEP_COLOR_YUV: { + color_conv = new ConvertYUVToRGBOperation(); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + if (color_conv) { converter.add_operation(color_conv); @@ -52,27 +84,4 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, } } -NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return nullptr; /* no conversion needed */ -} - -NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertHSVToRGBOperation(); -} - -NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const -{ - ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); - bNode *editor_node = this->get_bnode(); - operation->set_mode(editor_node->custom1); - return operation; -} - -NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertYUVToRGBOperation(); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h index 2bead24cb02..7576cc9eb2d 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.h +++ b/source/blender/compositor/nodes/COM_CombineColorNode.h @@ -12,45 +12,6 @@ class CombineColorNode : public Node { CombineColorNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; - - protected: - virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; -}; - -class CombineRGBANode : public CombineColorNode { - public: - CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineHSVANode : public CombineColorNode { - public: - CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineYCCANode : public CombineColorNode { - public: - CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineYUVANode : public CombineColorNode { - public: - CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc new file mode 100644 index 00000000000..d5ba379bfc2 --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#include "COM_CombineColorNodeLegacy.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +CombineColorNodeLegacy::CombineColorNodeLegacy(bNode *editor_node) : Node(editor_node) +{ +} + +void CombineColorNodeLegacy::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const +{ + NodeInput *input_rsocket = this->get_input_socket(0); + NodeInput *input_gsocket = this->get_input_socket(1); + NodeInput *input_bsocket = this->get_input_socket(2); + NodeInput *input_asocket = this->get_input_socket(3); + NodeOutput *output_socket = this->get_output_socket(0); + + CombineChannelsOperation *operation = new CombineChannelsOperation(); + if (input_rsocket->is_linked()) { + operation->set_canvas_input_index(0); + } + else if (input_gsocket->is_linked()) { + operation->set_canvas_input_index(1); + } + else if (input_bsocket->is_linked()) { + operation->set_canvas_input_index(2); + } + else { + operation->set_canvas_input_index(3); + } + converter.add_operation(operation); + + converter.map_input_socket(input_rsocket, operation->get_input_socket(0)); + converter.map_input_socket(input_gsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); + converter.map_input_socket(input_asocket, operation->get_input_socket(3)); + + NodeOperation *color_conv = get_color_converter(context); + if (color_conv) { + converter.add_operation(color_conv); + + converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0)); + converter.map_output_socket(output_socket, color_conv->get_output_socket()); + } + else { + converter.map_output_socket(output_socket, operation->get_output_socket()); + } +} + +NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return nullptr; /* no conversion needed */ +} + +NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertHSVToRGBOperation(); +} + +NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const +{ + ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); + return operation; +} + +NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertYUVToRGBOperation(); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h new file mode 100644 index 00000000000..edc66c8932f --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +class CombineColorNodeLegacy : public Node { + public: + CombineColorNodeLegacy(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; + + protected: + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; +}; + +class CombineRGBANode : public CombineColorNodeLegacy { + public: + CombineRGBANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineHSVANode : public CombineColorNodeLegacy { + public: + CombineHSVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineYCCANode : public CombineColorNodeLegacy { + public: + CombineYCCANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineYUVANode : public CombineColorNodeLegacy { + public: + CombineYUVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc index 221d80e67f2..a956c02ed42 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc @@ -12,7 +12,7 @@ SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node) } void SeparateColorNode::convert_to_operations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext &UNUSED(context)) const { NodeInput *image_socket = this->get_input_socket(0); NodeOutput *output_rsocket = this->get_output_socket(0); @@ -20,7 +20,39 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_bsocket = this->get_output_socket(2); NodeOutput *output_asocket = this->get_output_socket(3); - NodeOperation *color_conv = get_color_converter(context); + bNode *editor_node = this->get_bnode(); + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + + NodeOperation *color_conv = nullptr; + switch (storage->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case CMP_NODE_COMBSEP_COLOR_HSV: { + color_conv = new ConvertRGBToHSVOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_HSL: { + color_conv = new ConvertRGBToHSLOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_YCC: { + ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); + operation->set_mode(storage->ycc_mode); + color_conv = operation; + break; + } + case CMP_NODE_COMBSEP_COLOR_YUV: { + color_conv = new ConvertRGBToYUVOperation(); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + if (color_conv) { converter.add_operation(color_conv); @@ -84,27 +116,4 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, } } -NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return nullptr; /* no conversion needed */ -} - -NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertRGBToHSVOperation(); -} - -NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const -{ - ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); - bNode *editor_node = this->get_bnode(); - operation->set_mode(editor_node->custom1); - return operation; -} - -NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertRGBToYUVOperation(); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h index 78ab0959f43..6adb2d0bb22 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.h +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h @@ -12,45 +12,6 @@ class SeparateColorNode : public Node { SeparateColorNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; - - protected: - virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; -}; - -class SeparateRGBANode : public SeparateColorNode { - public: - SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateHSVANode : public SeparateColorNode { - public: - SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateYCCANode : public SeparateColorNode { - public: - SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateYUVANode : public SeparateColorNode { - public: - SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc new file mode 100644 index 00000000000..c3728bc152f --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#include "COM_SeparateColorNodeLegacy.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +SeparateColorNodeLegacy::SeparateColorNodeLegacy(bNode *editor_node) : Node(editor_node) +{ +} + +void SeparateColorNodeLegacy::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const +{ + NodeInput *image_socket = this->get_input_socket(0); + NodeOutput *output_rsocket = this->get_output_socket(0); + NodeOutput *output_gsocket = this->get_output_socket(1); + NodeOutput *output_bsocket = this->get_output_socket(2); + NodeOutput *output_asocket = this->get_output_socket(3); + + NodeOperation *color_conv = get_color_converter(context); + if (color_conv) { + converter.add_operation(color_conv); + + converter.map_input_socket(image_socket, color_conv->get_input_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(0); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_rsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(1); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_gsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(2); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_bsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(3); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_asocket, operation->get_output_socket(0)); + } +} + +NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return nullptr; /* no conversion needed */ +} + +NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertRGBToHSVOperation(); +} + +NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const +{ + ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); + return operation; +} + +NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertRGBToYUVOperation(); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h new file mode 100644 index 00000000000..10b33039c86 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +class SeparateColorNodeLegacy : public Node { + public: + SeparateColorNodeLegacy(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; + + protected: + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; +}; + +class SeparateRGBANode : public SeparateColorNodeLegacy { + public: + SeparateRGBANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateHSVANode : public SeparateColorNodeLegacy { + public: + SeparateHSVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateYCCANode : public SeparateColorNodeLegacy { + public: + SeparateYCCANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateYUVANode : public SeparateColorNodeLegacy { + public: + SeparateYUVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 7579abf792a..24c0c577ac7 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -464,6 +464,68 @@ void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<floa } } +/* ******** RGB to HSL ******** */ + +ConvertRGBToHSLOperation::ConvertRGBToHSLOperation() : ConvertBaseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); +} + +void ConvertRGBToHSLOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_hsl_v(input_color, output); + output[3] = input_color[3]; +} + +void ConvertRGBToHSLOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + rgb_to_hsl_v(in, it.out); + it.out[3] = in[3]; + } +} + +/* ******** HSL to RGB ******** */ + +ConvertHSLToRGBOperation::ConvertHSLToRGBOperation() : ConvertBaseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); +} + +void ConvertHSLToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + hsl_to_rgb_v(input_color, output); + output[0] = max_ff(output[0], 0.0f); + output[1] = max_ff(output[1], 0.0f); + output[2] = max_ff(output[2], 0.0f); + output[3] = input_color[3]; +} + +void ConvertHSLToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + hsl_to_rgb_v(in, it.out); + it.out[0] = max_ff(it.out[0], 0.0f); + it.out[1] = max_ff(it.out[1], 0.0f); + it.out[2] = max_ff(it.out[2], 0.0f); + it.out[3] = in[3]; + } +} + /* ******** Premul to Straight ******** */ ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation() diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index e1904d61d46..16d1e2e6bb5 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -172,6 +172,26 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation { void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; +class ConvertRGBToHSLOperation : public ConvertBaseOperation { + public: + ConvertRGBToHSLOperation(); + + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; +}; + +class ConvertHSLToRGBOperation : public ConvertBaseOperation { + public: + ConvertHSLToRGBOperation(); + + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; +}; + class ConvertPremulToStraightOperation : public ConvertBaseOperation { public: ConvertPremulToStraightOperation(); diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 573a740dac8..725751d15af 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -112,7 +112,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, double *X, *Y, *W; const unsigned int src_width = src->get_width(); const unsigned int src_height = src->get_height(); - unsigned int x, y, sz; + unsigned int x, y, src_dim_max; unsigned int i; float *buffer = src->get_buffer(); const uint8_t num_channels = src->get_num_channels(); @@ -202,10 +202,10 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, (void)0 /* Intermediate buffers. */ - sz = MAX2(src_width, src_height); - X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf"); - Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf"); - W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf"); + src_dim_max = MAX2(src_width, src_height); + X = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss X buf"); + Y = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss Y buf"); + W = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss W buf"); if (xy & 1) { /* H. */ int offset; for (y = 0; y < src_height; y++) { diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index ade75fa2f6f..12663e74d24 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -64,7 +64,7 @@ bool DEG_id_type_any_updated(const struct Depsgraph *depsgraph); bool DEG_id_type_any_exists(const struct Depsgraph *depsgraph, short id_type); /** Get additional evaluation flags for the given ID. */ -uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, struct ID *id); +uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, const struct ID *id); /** Get additional mesh CustomData_MeshMasks flags for the given object. */ void DEG_get_customdata_mask_for_object(const struct Depsgraph *graph, diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index fd569599b8b..6ffc711a475 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -32,6 +32,49 @@ #include "intern/eval/deg_eval_copy_on_write.h" #include "intern/node/deg_node_id.h" +namespace blender::deg { + +static const ID *get_original_id(const ID *id) +{ + if (id == nullptr) { + return nullptr; + } + if (id->orig_id == nullptr) { + return id; + } + BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); + return (ID *)id->orig_id; +} + +static ID *get_original_id(ID *id) +{ + const ID *const_id = id; + return const_cast<ID *>(get_original_id(const_id)); +} + +static const ID *get_evaluated_id(const Depsgraph *deg_graph, const ID *id) +{ + if (id == nullptr) { + return nullptr; + } + /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), + * but here we never do assert, since we don't know nature of the + * incoming ID data-block. */ + const IDNode *id_node = deg_graph->find_id_node(id); + if (id_node == nullptr) { + return id; + } + return id_node->id_cow; +} + +static ID *get_evaluated_id(const Depsgraph *deg_graph, ID *id) +{ + const ID *const_id = id; + return const_cast<ID *>(get_evaluated_id(deg_graph, const_id)); +} + +} // namespace blender::deg + namespace deg = blender::deg; struct Scene *DEG_get_input_scene(const Depsgraph *graph) @@ -90,7 +133,7 @@ bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type) return deg_graph->id_type_exist[BKE_idtype_idcode_to_index(id_type)] != 0; } -uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) +uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, const ID *id) { if (graph == nullptr) { /* Happens when converting objects to mesh from a python script @@ -102,7 +145,7 @@ uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) } const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph); - const deg::IDNode *id_node = deg_graph->find_id_node(DEG_get_original_id(id)); + const deg::IDNode *id_node = deg_graph->find_id_node(deg::get_original_id(id)); if (id_node == nullptr) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; @@ -171,18 +214,7 @@ Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object) ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id) { - if (id == nullptr) { - return nullptr; - } - /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), - * but here we never do assert, since we don't know nature of the - * incoming ID data-block. */ - const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph; - const deg::IDNode *id_node = deg_graph->find_id_node(id); - if (id_node == nullptr) { - return id; - } - return id_node->id_cow; + return deg::get_evaluated_id(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id); } void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph, @@ -249,14 +281,7 @@ Object *DEG_get_original_object(Object *object) ID *DEG_get_original_id(ID *id) { - if (id == nullptr) { - return nullptr; - } - if (id->orig_id == nullptr) { - return id; - } - BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); - return (ID *)id->orig_id; + return deg::get_original_id(id); } bool DEG_is_original_id(const ID *id) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index ed90e77572e..f72112d7c54 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -421,8 +421,8 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl - intern/draw_shader_shared.h intern/draw_common_shader_shared.h + intern/draw_shader_shared.h engines/gpencil/shaders/gpencil_frag.glsl engines/gpencil/shaders/gpencil_vert.glsl diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 4f562dd9804..0b9909a904b 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -95,7 +95,7 @@ typedef struct EEVEE_LightBake { /** Target layer to store the data to. */ int layer; /** Sample count for the convolution. */ - float samples_ct, invsamples_ct; + float samples_count, invsamples_count; /** Sampling bias during convolution step. */ float lod_factor; /** Max cube-map LOD to sample when convolving. */ @@ -282,14 +282,14 @@ static void irradiance_pool_size_get(int visibility_size, int total_samples, int (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); /* The irradiance itself take one layer, hence the +1 */ - int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + int layer_count = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); - int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + int texel_count = (int)ceilf((float)total_samples / (float)(layer_count - 1)); r_size[0] = visibility_size * - max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + max_ii(1, min_ii(texel_count, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); r_size[1] = visibility_size * - max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_ct; + max_ii(1, (texel_count / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[2] = layer_count; } static bool EEVEE_lightcache_validate(const LightCache *light_cache, diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index e6c33feeeb9..a4bd789438d 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -64,7 +64,7 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD DRW_PASS_CREATE(psl->lookdev_diffuse_pass, state); grp = DRW_shgroup_create(sh, psl->lookdev_diffuse_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, -1.0f, false, false); DRW_shgroup_add_material_resources(grp, gpumat); DRW_shgroup_call(grp, sphere, NULL); } @@ -75,7 +75,7 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD DRW_PASS_CREATE(psl->lookdev_glossy_pass, state); grp = DRW_shgroup_create(sh, psl->lookdev_glossy_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, -1.0f, false, false); DRW_shgroup_add_material_resources(grp, gpumat); DRW_shgroup_call(grp, sphere, NULL); } @@ -112,7 +112,7 @@ void EEVEE_lookdev_init(EEVEE_Data *vedata) if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] || rect->ymin != effects->anchor[1]) { - /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */ + /* Make sphere resolution adaptive to viewport_scale, DPI and #U.lookdev_sphere_size. */ float res_scale = clamp_f( (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index a81d3e56673..efd27c19654 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -67,6 +67,7 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, EEVEE_Data *vedata, const int *ssr_id, const float *refract_depth, + const float alpha_clip_threshold, bool use_ssrefraction, bool use_alpha_blend) { @@ -91,6 +92,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block_ref(shgrp, "renderpass_block", &pd->renderpass_ubo); + DRW_shgroup_uniform_float_copy(shgrp, "alphaClipThreshold", alpha_clip_threshold); + DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1); DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); if (use_diffuse || use_glossy || use_refract) { @@ -480,6 +483,8 @@ BLI_INLINE void material_shadow(EEVEE_Data *vedata, /* Shadow Pass */ const bool use_shadow_shader = ma->use_nodes && ma->nodetree && ELEM(ma->blend_shadow, MA_BS_CLIP, MA_BS_HASHED); + float alpha_clip_threshold = (ma->blend_shadow == MA_BS_CLIP) ? ma->alpha_threshold : -1.0f; + int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH; SET_FLAG_FROM_TEST(mat_options, use_shadow_shader, VAR_MAT_HASH); SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); @@ -503,7 +508,8 @@ BLI_INLINE void material_shadow(EEVEE_Data *vedata, } else { *grp_p = grp = DRW_shgroup_create(sh, psl->shadow_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources( + grp, gpumat, sldata, vedata, NULL, NULL, alpha_clip_threshold, false, false); } DRW_shgroup_add_material_resources(grp, gpumat); @@ -533,6 +539,7 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && ((effects->enabled_effects & EFFECT_REFRACT) != 0); const bool use_depth_shader = use_gpumat && ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED); + float alpha_clip_threshold = (ma->blend_method == MA_BM_CLIP) ? ma->alpha_threshold : -1.0f; /* HACK: Assume the struct will never be smaller than our variations. * This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */ @@ -581,7 +588,8 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, } else { *grp_p = grp = DRW_shgroup_create(sh, depth_ps); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources( + grp, gpumat, sldata, vedata, NULL, NULL, alpha_clip_threshold, false, false); } DRW_shgroup_add_material_resources(grp, gpumat); @@ -630,8 +638,15 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, } else { *grp_p = grp = DRW_shgroup_create(sh, shading_pass); - EEVEE_material_bind_resources( - grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, false); + EEVEE_material_bind_resources(grp, + gpumat, + sldata, + vedata, + &ssr_id, + &ma->refract_depth, + alpha_clip_threshold, + use_ssrefract, + false); } DRW_shgroup_add_material_resources(grp, gpumat); @@ -677,7 +692,7 @@ static EeveeMaterialCache material_transparent(EEVEE_Data *vedata, DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->transparent_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, true); + EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, -1.0f, false, true); DRW_shgroup_add_material_resources(grp, gpumat); cur_state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; @@ -699,7 +714,7 @@ static EeveeMaterialCache material_transparent(EEVEE_Data *vedata, psl->transparent_pass); EEVEE_material_bind_resources( - grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, true); + grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, -1.0f, use_ssrefract, true); DRW_shgroup_add_material_resources(grp, gpumat); cur_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 3a86640134c..6b9dc6fb017 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -1141,6 +1141,7 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, EEVEE_Data *vedata, const int *ssr_id, const float *refract_depth, + const float alpha_clip_threshold, bool use_ssrefraction, bool use_alpha_blend); /* eevee_lights.c */ diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl index fa94b5ed272..0f5290a7c07 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl @@ -5,7 +5,7 @@ #pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) #pragma BLENDER_REQUIRE(renderpass_lib.glsl) -#ifdef USE_SHADER_TO_RGBA +#if defined(USE_SHADER_TO_RGBA) || defined(USE_ALPHA_BLEND) bool do_sss = false; bool do_ssr = false; #else @@ -309,16 +309,19 @@ vec4 closure_to_rgba(Closure closure) return vec4(closure.radiance, 1.0 - saturate(avg(closure.transmittance))); } -Closure closure_add(Closure cl1, Closure cl2) +Closure closure_add(inout Closure cl1, inout Closure cl2) { Closure cl; cl.radiance = cl1.radiance + cl2.radiance; cl.transmittance = cl1.transmittance + cl2.transmittance; cl.holdout = cl1.holdout + cl2.holdout; + /* Make sure each closure is only added once to the result. */ + cl1 = CLOSURE_DEFAULT; + cl2 = CLOSURE_DEFAULT; return cl; } -Closure closure_mix(Closure cl1, Closure cl2, float fac) +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac) { /* Weights have already been applied. */ return closure_add(cl1, cl2); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl index e450b8ad3c8..5e34d654cfd 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl @@ -92,7 +92,7 @@ vec4 closure_to_rgba(Closure closure) return vec4(0.0); } -Closure closure_mix(Closure cl1, Closure cl2, float fac) +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac) { Closure cl; cl.absorption = mix(cl1.absorption, cl2.absorption, fac); @@ -102,7 +102,7 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac) return cl; } -Closure closure_add(Closure cl1, Closure cl2) +Closure closure_add(inout Closure cl1, inout Closure cl2) { Closure cl; cl.absorption = cl1.absorption + cl2.absorption; diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl index 492b78a20b6..21d347942ca 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl @@ -49,7 +49,7 @@ struct Closure { #ifndef GPU_METAL /* Prototype */ Closure nodetree_exec(); -vec4 closure_to_rgba(Closure); +vec4 closure_to_rgba(Closure cl); void output_aov(vec4 color, float value, uint hash); vec3 coordinate_camera(vec3 P); vec3 coordinate_screen(vec3 P); @@ -87,8 +87,8 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection clearcoat, ClosureRefraction refraction); -Closure closure_add(Closure cl1, Closure cl2); -Closure closure_mix(Closure cl1, Closure cl2, float fac); +Closure closure_add(inout Closure cl1, inout Closure cl2); +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac); float ambient_occlusion_eval(vec3 normal, float distance, diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index c8eea8d7860..15c68dc5829 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -11,6 +11,8 @@ #pragma BLENDER_REQUIRE(surface_lib.glsl) #ifdef USE_ALPHA_HASH +/* A value of -1.0 will disable alpha clip and use alpha hash. */ +uniform float alphaClipThreshold; /* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */ float hash(vec2 a) @@ -76,9 +78,16 @@ void main() float opacity = saturate(1.0 - avg(cl.transmittance)); - /* Hashed Alpha Testing */ - if (opacity < hashed_alpha_threshold(worldPosition)) { - discard; + if (alphaClipThreshold == -1.0) { + /* NOTE: uniform control flow required for dFdx(). */ + if (opacity < hashed_alpha_threshold(worldPosition)) { + discard; + } + } + else { + if (opacity <= alphaClipThreshold) { + discard; + } } #endif } diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 1f2f7cb65cc..696e5d4c97b 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -112,6 +112,7 @@ GlobalData init_globals(void) surf.N = -surf.N; } # ifdef HAIR_SHADER + vec3 V = cameraVec(surf.P); /* Shade as a cylinder. */ vec3 B = normalize(cross(worldNormal, hairTangent)); float cos_theta; @@ -125,7 +126,10 @@ GlobalData init_globals(void) } float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta); - surf.T = hairTangent; + surf.curve_T = -hairTangent; + /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ + surf.curve_B = cross(V, surf.curve_T); + surf.curve_N = safe_normalize(cross(surf.curve_T, surf.curve_B)); surf.is_strand = true; surf.hair_time = hairTime; surf.hair_thickness = hairThickness; @@ -134,7 +138,7 @@ GlobalData init_globals(void) surf.barycentric_coords = hair_resolve_barycentric(hairBary); # endif # else - surf.T = vec3(0.0); + surf.curve_T = surf.curve_B = surf.curve_N = vec3(0.0); surf.is_strand = false; surf.hair_time = 0.0; surf.hair_thickness = 0.0; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index b574e8cdb4c..ce863bdf660 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -57,3 +57,23 @@ float F_eta(float a, float b) { return 0.0; } + +vec3 coordinate_camera(vec3 P) +{ + return vec3(0.0); +} + +vec3 coordinate_screen(vec3 P) +{ + return vec3(0.0); +} + +vec3 coordinate_reflect(vec3 P, vec3 N) +{ + return vec3(0.0); +} + +vec3 coordinate_incoming(vec3 P) +{ + return vec3(0.0); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index b369e536454..8fef1cf7fc5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -54,7 +54,7 @@ void Instance::init(const int2 &output_res, main_view.init(output_res); } -void Instance::update_eval_members(void) +void Instance::update_eval_members() { scene = DEG_get_evaluated_scene(depsgraph); view_layer = DEG_get_evaluated_view_layer(depsgraph); @@ -133,11 +133,11 @@ void Instance::object_sync(Object *ob) ob_handle.reset_recalc_flag(); } -void Instance::end_sync(void) +void Instance::end_sync() { } -void Instance::render_sync(void) +void Instance::render_sync() { } @@ -151,7 +151,7 @@ void Instance::render_sync(void) * Conceptually renders one sample per pixel. * Everything based on random sampling should be done here (i.e: DRWViews jitter) **/ -void Instance::render_sample(void) +void Instance::render_sample() { main_view.render(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 0f085b0952a..c3cf08c8390 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -74,21 +74,21 @@ class Instance { const View3D *v3d = nullptr, const RegionView3D *rv3d = nullptr); - void begin_sync(void); + void begin_sync(); void object_sync(Object *ob); - void end_sync(void); + void end_sync(); - void render_sync(void); + void render_sync(); void render_frame(RenderLayer *render_layer, const char *view_name); void draw_viewport(DefaultFramebufferList *dfbl); private: - void render_sample(void); + void render_sample(); void mesh_sync(Object *ob, ObjectHandle &ob_handle); - void update_eval_members(void); + void update_eval_members(); }; } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index f3132185683..7452e5c26a4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -154,7 +154,7 @@ MaterialModule::~MaterialModule() BKE_id_free(nullptr, error_mat_); } -void MaterialModule::begin_sync(void) +void MaterialModule::begin_sync() { queued_shaders_count_ = 0; diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 5c8188c7cf0..af9ff6bf6ba 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -115,7 +115,7 @@ struct MaterialKey { options = shader_uuid_from_material_type(surface_pipeline, geometry); } - uint64_t hash(void) const + uint64_t hash() const { BLI_assert(options < sizeof(*mat)); return (uint64_t)mat + options; @@ -150,7 +150,7 @@ struct ShaderKey { options = (options << 16u) | shader_closure_bits_from_flag(gpumat); } - uint64_t hash(void) const + uint64_t hash() const { return (uint64_t)shader + options; } @@ -238,7 +238,7 @@ class MaterialModule { MaterialModule(Instance &inst); ~MaterialModule(); - void begin_sync(void); + void begin_sync(); MaterialArray &material_array_get(Object *ob); Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 96415f6ae0f..e31372e770d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -36,7 +36,7 @@ void WorldPipeline::sync(GPUMaterial *gpumat) DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat.ptr()); } -void WorldPipeline::render(void) +void WorldPipeline::render() { DRW_draw_pass(world_ps_); } @@ -49,7 +49,7 @@ void WorldPipeline::render(void) * NPR materials (using Closure to RGBA) or material using ALPHA_BLEND. * \{ */ -void ForwardPipeline::sync(void) +void ForwardPipeline::sync() { { DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 14eac812e55..a5a6847f62e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -37,7 +37,7 @@ class WorldPipeline { WorldPipeline(Instance &inst) : inst_(inst){}; void sync(GPUMaterial *gpumat); - void render(void); + void render(); }; /** \} */ @@ -63,7 +63,7 @@ class ForwardPipeline { public: ForwardPipeline(Instance &inst) : inst_(inst){}; - void sync(void); + void sync(); DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat) { diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index 3f58ca14778..34357193d3e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -77,7 +77,7 @@ struct ObjectKey { ObjectKey(Object *ob) : ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){}; - uint64_t hash(void) const + uint64_t hash() const { return hash_value; } @@ -121,7 +121,7 @@ struct ObjectKey { struct ObjectHandle : public DrawData { ObjectKey object_key; - void reset_recalc_flag(void) + void reset_recalc_flag() { if (recalc != 0) { recalc = 0; @@ -130,7 +130,7 @@ struct ObjectHandle : public DrawData { }; struct WorldHandle : public DrawData { - void reset_recalc_flag(void) + void reset_recalc_flag() { if (recalc != 0) { recalc = 0; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 074bc8b02aa..de7341f814b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -74,6 +74,7 @@ void ShadingView::sync(int2 render_extent_) } #else /* TEMP */ + UNUSED_VARS(face_matrix_); const DRWView *default_view = DRW_view_default_get(); DRW_view_winmat_get(default_view, winmat.ptr(), false); DRW_view_viewmat_get(default_view, viewmat.ptr(), false); @@ -96,7 +97,7 @@ void ShadingView::sync(int2 render_extent_) postfx_tx_.sync(); } -void ShadingView::render(void) +void ShadingView::render() { if (!is_enabled_) { return; @@ -180,7 +181,7 @@ GPUTexture *ShadingView::render_post(GPUTexture *input_tx) return input_tx; } -void ShadingView::update_view(void) +void ShadingView::update_view() { float4x4 viewmat, winmat; DRW_view_viewmat_get(main_view_, viewmat.ptr(), false); diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index e0ba4485365..e78a3222d8b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -73,16 +73,16 @@ class ShadingView { ~ShadingView(){}; - void init(void); + void init(); void sync(int2 render_extent_); - void render(void); + void render(); GPUTexture *render_post(GPUTexture *input_tx); private: - void update_view(void); + void update_view(); }; /** \} */ @@ -135,14 +135,14 @@ class MainView { } } - void sync(void) + void sync() { for (auto i : IndexRange(6)) { shading_views_[i].sync(render_extent_); } } - void render(void) + void render() { for (auto i : IndexRange(6)) { shading_views_[i].render(); diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh index ff5be774b90..05177928436 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.hh +++ b/source/blender/draw/engines/eevee_next/eevee_world.hh @@ -56,7 +56,7 @@ class World { public: World(Instance &inst) : inst_(inst){}; - void sync(void); + void sync(); }; /** \} */ 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 abecb373995..3c5acf62e30 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 @@ -164,7 +164,7 @@ float attr_load_float(samplerBuffer cd_buf) * \{ */ # ifndef OBINFO_LIB -# error "draw_object_infos is mandatory for volume objects" +# error draw_object_infos is mandatory for volume objects # endif vec3 g_orco; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index edf9e39e005..708bd153e84 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -6,7 +6,7 @@ #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) -void main(void) +void main() { init_interface(); @@ -18,7 +18,7 @@ void main(void) ViewMatrixInverse[3].xyz, ViewMatrixInverse[2].xyz, interp.P, - T, + interp.curves_tangent, interp.curves_binormal, interp.curves_time, interp.curves_thickness, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl index bb1f93b4453..5b404ec5237 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl @@ -4,7 +4,7 @@ #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) -void main(void) +void main() { init_interface(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl index 24d5a2c60b0..7b38057f41a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -4,7 +4,7 @@ #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) -void main(void) +void main() { init_interface(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl index 6ce98f26c7e..cbf85ca56a9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl @@ -7,7 +7,7 @@ #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) -void main(void) +void main() { /* Fullscreen triangle. */ int v = gl_VertexID % 3; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 06191a5c007..277b2e35d8b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -9,6 +9,7 @@ float g_holdout; /* The Closure type is never used. Use float as dummy type. */ #define Closure float +#define CLOSURE_DEFAULT 0.0 /* Sampled closure parameters. */ ClosureDiffuse g_diffuse_data; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl index 5500131c3e7..7848c4a0611 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl @@ -11,7 +11,7 @@ #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) -void main(void) +void main() { init_globals(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl index a635cf72e45..002eed91130 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl @@ -53,7 +53,7 @@ float hashed_alpha_threshold(float hash_scale, float hash_offset, vec3 P) return threshold; } -void main(void) +void main() { #ifdef MAT_TRANSPARENT init_globals(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index b3fbdb937f9..143e88dbe68 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -43,7 +43,7 @@ vec4 closure_to_rgba(Closure cl) return out_color; } -void main(void) +void main() { init_globals(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl index 01a90bcb81b..30b48edaa78 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl @@ -27,7 +27,7 @@ vec3 barycentric_distances_get() } #endif -void init_globals_mesh(void) +void init_globals_mesh() { #if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH) g_data.barycentric_coords = gpu_BaryCoord.xy; @@ -35,13 +35,19 @@ void init_globals_mesh(void) #endif } -void init_globals_curves(void) +void init_globals_curves() { /* Shade as a cylinder. */ float cos_theta = interp.curves_time_width / interp.curves_thickness; float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta); + /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ + vec3 V = cameraVec(g_data.P); + g_data.curve_T = -interp.curves_tangent; + g_data.curve_B = cross(V, g_data.curve_T); + g_data.curve_N = safe_normalize(cross(g_data.curve_T, g_data.curve_B)); + g_data.is_strand = true; g_data.hair_time = interp.curves_time; g_data.hair_thickness = interp.curves_thickness; @@ -51,13 +57,13 @@ void init_globals_curves(void) #endif } -void init_globals_gpencil(void) +void init_globals_gpencil() { /* Undo backface flip as the gpencil normal is already pointing towards the camera. */ g_data.N = interp.N; } -void init_globals(void) +void init_globals() { /* Default values. */ g_data.P = interp.P; @@ -94,6 +100,7 @@ void init_interface() interp.P = vec3(0.0); interp.N = vec3(0.0); interp.barycentric_coords = vec2(0.0); + interp.curves_tangent = vec3(0.0); interp.curves_binormal = vec3(0.0); interp.curves_time = 0.0; interp.curves_time_width = 0.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl index 9901596623d..ac657afc922 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl @@ -11,7 +11,7 @@ #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) -void main(void) +void main() { init_globals(); /* View position is passed to keep accuracy. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index cd297de5719..ccd67360073 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -54,6 +54,7 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") .smooth(Type::VEC3, "P") .smooth(Type::VEC3, "N") .smooth(Type::VEC2, "barycentric_coords") + .smooth(Type::VEC3, "curves_tangent") .smooth(Type::VEC3, "curves_binormal") .smooth(Type::FLOAT, "curves_time") .smooth(Type::FLOAT, "curves_time_width") diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 19afdb3de5a..c7ef8677336 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -61,10 +61,10 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, /* Depth need to be remapped to [0..1] range. */ pix_z = MEM_dupallocN(pix_z); - int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + int pix_num = rpass_z_src->rectx * rpass_z_src->recty; if (DRW_view_is_persp_get(view)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); } @@ -74,7 +74,7 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, float near = DRW_view_near_distance_get(view); float far = DRW_view_far_distance_get(view); float range_inv = 1.0f / fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (pix_z[i] + near) * range_inv; pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); } @@ -172,11 +172,11 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert GPU depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -192,7 +192,7 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh index cb84c7f14ad..358e6fd3bd9 100644 --- a/source/blender/draw/engines/image/image_instance_data.hh +++ b/source/blender/draw/engines/image/image_instance_data.hh @@ -75,8 +75,10 @@ struct IMAGE_InstanceData { TextureInfo &info = texture_infos[i]; const bool is_allocated = info.texture != nullptr; const bool is_visible = info.visible; - const bool should_be_freed = !is_visible && is_allocated; - const bool should_be_created = is_visible && !is_allocated; + const bool resolution_changed = assign_if_different(info.last_viewport_size, + float2(DRW_viewport_size_get())); + const bool should_be_freed = is_allocated && (!is_visible || resolution_changed); + const bool should_be_created = is_visible && (!is_allocated || resolution_changed); if (should_be_freed) { GPU_texture_free(info.texture); diff --git a/source/blender/draw/engines/image/image_texture_info.hh b/source/blender/draw/engines/image/image_texture_info.hh index cd51cdaff3c..9a75941c533 100644 --- a/source/blender/draw/engines/image/image_texture_info.hh +++ b/source/blender/draw/engines/image/image_texture_info.hh @@ -46,6 +46,8 @@ struct TextureInfo { */ GPUTexture *texture; + float2 last_viewport_size = float2(0.0f, 0.0f); + ~TextureInfo() { if (batch != nullptr) { diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 5c5226bfe65..9531b0dd983 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -124,7 +124,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) } } - /* Handles and curve point for Curve Edit submode. */ + /* Handles and curve point for Curve Edit sub-mode. */ if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) { DRWState state = DRW_STATE_WRITE_COLOR; DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state); @@ -297,7 +297,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) } const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - int line_ct = gridlines * 4 + 2; + const int line_count = gridlines * 4 + 2; DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; state |= (grid_xray) ? DRW_STATE_DEPTH_ALWAYS : DRW_STATE_DEPTH_LESS_EQUAL; @@ -311,8 +311,8 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); - DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); - DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_count / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_count); } } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 1279682e899..e5dcf6c5624 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -115,11 +115,11 @@ static void workbench_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert ogl depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -135,7 +135,7 @@ static void workbench_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 2c902e9b627..ce7773e7439 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -124,12 +124,12 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, double noise_ofs; BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); float dim[3], step_length, max_slice; - float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); + float slice_count[3] = {fds->res[0], fds->res[1], fds->res[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, fds->slice_per_voxel)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); BKE_object_dimensions_get(ob, dim); - invert_v3(slice_ct); - mul_v3_v3(dim, slice_ct); + invert_v3(slice_count); + mul_v3_v3(dim, slice_count); step_length = len_v3(dim); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); @@ -273,12 +273,12 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, float step_length, max_slice; int resolution[3]; GPU_texture_get_mipmap_size(grid->texture, 0, resolution); - float slice_ct[3] = {resolution[0], resolution[1], resolution[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); - invert_v3(slice_ct); - mul_v3_v3(slice_ct, world_size); - step_length = len_v3(slice_ct); + float slice_count[3] = {resolution[0], resolution[1], resolution[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, 5.0f)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); + invert_v3(slice_count); + mul_v3_v3(slice_count, world_size); + step_length = len_v3(slice_count); /* Set uniforms. */ grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 712118e8282..572b87282e9 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -430,12 +430,12 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, DRW_shgroup_call_ex(shgroup, ob, NULL, geom, true, NULL) void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_num); /** * A count of 0 instance will use the default number of instance in the batch. */ void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num); void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 4567e470146..cb6006e303a 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -328,7 +328,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], bool do_final, bool do_uvedit, - bool use_subsurf_fdots, const Scene *scene, const struct ToolSettings *ts, bool use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 8a7b4fc9703..ec544d8e786 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -563,7 +563,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -687,7 +686,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshRenderData *mr = mesh_render_data_create( object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_subsurf_fdots = mr->me && mr->me->runtime.subsurf_face_dot_tags != nullptr; mr->use_final_mesh = do_final; #ifdef DEBUG_TIME @@ -919,7 +918,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -935,7 +933,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, obmat, do_final, do_uvedit, - use_subsurf_fdots, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 7b8f34b999c..ebcdabe4942 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -108,7 +108,7 @@ static void curve_eval_render_wire_verts_edges_len_get(const blender::bke::Curve const blender::VArray<bool> cyclic = curves.cyclic(); for (const int i : curves.curves_range()) { const IndexRange points = curves.evaluated_points_for_curve(i); - *r_edge_len += blender::bke::curves::curve_segment_size(points.size(), cyclic[i]); + *r_edge_len += blender::bke::curves::curve_segment_num(points.size(), cyclic[i]); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index f2742f3bcc7..1896df7c650 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -37,6 +37,7 @@ using blender::float3; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; /* ---------------------------------------------------------------------- */ @@ -147,46 +148,50 @@ static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_ca return; } - curves_cache.strands_len = curves.geometry.curve_size; - curves_cache.elems_len = curves.geometry.point_size + curves.geometry.curve_size; - curves_cache.point_len = curves.geometry.point_size; + curves_cache.strands_len = curves.geometry.curve_num; + curves_cache.elems_len = curves.geometry.point_num + curves.geometry.curve_num; + curves_cache.point_len = curves.geometry.point_num; } -static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, - GPUVertBufRaw &attr_step, - GPUVertBufRaw &length_step) +struct PositionAndParameter { + float3 position; + float parameter; +}; + +static void curves_batch_cache_fill_segments_proc_pos( + const Curves &curves_id, + MutableSpan<PositionAndParameter> posTime_data, + MutableSpan<float> hairLength_data) { /* TODO: use hair radius layer if available. */ - const int curve_size = curves_id.geometry.curve_size; + const int curve_num = curves_id.geometry.curve_num; const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_id.geometry); Span<float3> positions = curves.positions(); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range = curves.points_for_curve(i); + for (const int i_curve : IndexRange(curve_num)) { + const IndexRange points = curves.points_for_curve(i_curve); + + Span<float3> curve_positions = positions.slice(points); + MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - Span<float3> curve_positions = positions.slice(curve_range); float total_len = 0.0f; - float *seg_data_first; - for (const int i_curve : curve_positions.index_range()) { - float *seg_data = (float *)GPU_vertbuf_raw_step(&attr_step); - copy_v3_v3(seg_data, curve_positions[i_curve]); - if (i_curve == 0) { - seg_data_first = seg_data; - } - else { - total_len += blender::math::distance(curve_positions[i_curve - 1], - curve_positions[i_curve]); + for (const int i_point : curve_positions.index_range()) { + if (i_point > 0) { + total_len += blender::math::distance(curve_positions[i_point - 1], + curve_positions[i_point]); } - seg_data[3] = total_len; + curve_posTime_data[i_point].position = curve_positions[i_point]; + curve_posTime_data[i_point].parameter = total_len; } + hairLength_data[i_curve] = total_len; + /* Assign length value. */ - *(float *)GPU_vertbuf_raw_step(&length_step) = total_len; if (total_len > 0.0f) { + const float factor = 1.0f / total_len; /* Divide by total length to have a [0-1] number. */ - for ([[maybe_unused]] const int i_curve : curve_positions.index_range()) { - seg_data_first[3] /= total_len; - seg_data_first += 4; + for (const int i_point : curve_positions.index_range()) { + curve_posTime_data[i_point].parameter *= factor; } } } @@ -199,26 +204,26 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { /* Initialize vertex format. */ GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "pos"); cache.proc_point_buf = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len); - GPUVertBufRaw point_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_point_buf, pos_id, &point_step); + MutableSpan posTime_data{ + reinterpret_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), + cache.point_len}; GPUVertFormat length_format = {0}; - uint length_id = GPU_vertformat_attr_add( - &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); cache.proc_length_buf = GPU_vertbuf_create_with_format(&length_format); GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len); - GPUVertBufRaw length_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_length_buf, length_id, &length_step); + MutableSpan hairLength_data{ + reinterpret_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), cache.strands_len}; - curves_batch_cache_fill_segments_proc_pos(curves, point_step, length_step); + curves_batch_cache_fill_segments_proc_pos(curves, posTime_data, hairLength_data); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache.proc_point_buf); @@ -307,7 +312,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, const int res, GPUIndexBufBuilder &elb) { - const int curves_num = curves.geometry.curve_size; + const int curves_num = curves.geometry.curve_num; uint curr_point = 0; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 917216b92e6..a406e154fa0 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -2128,8 +2128,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX); - const bool use_subsurf_fdots = me->runtime.subsurf_face_dot_tags != NULL; - if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, @@ -2142,7 +2140,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, false, true, - false, scene, ts, true); @@ -2160,7 +2157,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, false, false, - use_subsurf_fdots, scene, ts, true); @@ -2178,7 +2174,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, true, false, - use_subsurf_fdots, ts, use_hide); } @@ -2199,7 +2194,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, true, false, - use_subsurf_fdots, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 19745304b2d..8eb0509d615 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1143,7 +1143,7 @@ static void draw_subdiv_init_ubo_storage(const DRWSubdivCache *cache, ubo->max_patch_face = cache->gpu_patch_map.max_patch_face; ubo->max_depth = cache->gpu_patch_map.max_depth; ubo->patches_are_triangular = cache->gpu_patch_map.patches_are_triangular; - ubo->coarse_poly_count = cache->bm ? cache->bm->totface : cache->num_coarse_poly; + ubo->coarse_poly_count = cache->num_coarse_poly; ubo->optimal_display = cache->optimal_display; ubo->num_subdiv_loops = cache->num_subdiv_loops; ubo->edge_loose_offset = cache->num_subdiv_loops * 2; @@ -1904,7 +1904,6 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool /*use_subsurf_fdots*/, const ToolSettings *ts, const bool /*use_hide*/, OpenSubdiv_EvaluatorCache *evaluator_cache) @@ -2103,7 +2102,6 @@ void DRW_create_subdivision(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const ToolSettings *ts, const bool use_hide) { @@ -2128,7 +2126,6 @@ void DRW_create_subdivision(const Scene *scene, obmat, do_final, do_uvedit, - use_subsurf_fdots, ts, use_hide, g_evaluator_cache)) { diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 88118361115..2edf596ac63 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -209,7 +209,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; + float hair_rad_shape = 0.0f; float hair_rad_root = 0.005f; float hair_rad_tip = 0.0f; bool hair_close_tip = true; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 6ab8d30109e..f71d88342f1 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -529,7 +529,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s dst->view_data_active = dst->vmempool->view_data[view]; dst->resource_handle = 0; dst->pass_handle = 0; - dst->primary_view_ct = 0; + dst->primary_view_num = 0; drw_viewport_data_reset(dst->vmempool); @@ -1287,7 +1287,7 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d); - if (G.is_rendering) { + if (G.is_rendering && U.experimental.use_draw_manager_acquire_lock) { return; } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 7a9585262ff..2d0837370b2 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -617,7 +617,7 @@ typedef struct DRWManager { DRWView *view_default; DRWView *view_active; DRWView *view_previous; - uint primary_view_ct; + uint primary_view_num; /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ ViewInfos view_storage_cpy; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index b5432da0957..b0d8017940f 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -937,25 +937,25 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, } void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct) + DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct); + drw_command_draw_range(shgroup, geom, handle, v_sta, v_num); } void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct) + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); + drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_num); } void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, @@ -1905,8 +1905,8 @@ DRWView *DRW_view_create(const float viewmat[4][4], { DRWView *view = BLI_memblock_alloc(DST.vmempool->views); - if (DST.primary_view_ct < MAX_CULLED_VIEWS) { - view->culling_mask = 1u << DST.primary_view_ct++; + if (DST.primary_view_num < MAX_CULLED_VIEWS) { + view->culling_mask = 1u << DST.primary_view_num++; } else { BLI_assert(0); diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c index ba348d10c83..063aec24b94 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.c @@ -43,8 +43,13 @@ static GPUShader *hair_refine_shader_transform_feedback_create( char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; - sh = DRW_shader_create_with_transform_feedback( - shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); + sh = DRW_shader_create_with_transform_feedback(shader_src, + NULL, + "#define HAIR_PHASE_SUBDIV\n" + "#define USE_TF\n", + GPU_SHADER_TFB_POINTS, + var_names, + 1); MEM_freeN(shader_src); return sh; @@ -53,19 +58,7 @@ static GPUShader *hair_refine_shader_transform_feedback_create( static GPUShader *hair_refine_shader_transform_feedback_workaround_create( ParticleRefineShader UNUSED(refinement)) { - GPUShader *sh = NULL; - - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); - sh = DRW_shader_create(shader_src, - NULL, - datatoc_gpu_shader_3D_smooth_color_frag_glsl, - "#define blender_srgb_to_framebuffer_space(a) a\n" - "#define HAIR_PHASE_SUBDIV\n" - "#define TF_WORKAROUND\n"); - MEM_freeN(shader_src); - - return sh; + return GPU_shader_create_from_info_name("draw_hair_refine_transform_feedback_workaround"); } GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index d803fad5052..2d24d07e037 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -160,8 +160,8 @@ typedef struct DRWSubdivCache { /* Contains the start loop index and the smooth flag for each coarse polygon. */ struct GPUVertBuf *extra_coarse_face_data; - /* Computed for ibo.points, one value per subdivided vertex, mapping coarse vertices -> - * subdivided loop */ + /* Computed for `ibo.points`, one value per subdivided vertex, + * mapping coarse vertices -> subdivided loop. */ int *point_indices; /* Material offsets. */ @@ -194,7 +194,6 @@ void DRW_create_subdivision(const struct Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const ToolSettings *ts, const bool use_hide); diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc index 0e55d28f6df..3dc28dc9a9a 100644 --- a/source/blender/draw/intern/draw_view_data.cc +++ b/source/blender/draw/intern/draw_view_data.cc @@ -88,7 +88,7 @@ void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewpo }); } -static void draw_viewport_engines_data_clear(ViewportEngineData *data) +static void draw_viewport_engines_data_clear(ViewportEngineData *data, bool clear_instance_data) { DrawEngineType *engine_type = data->engine_type->draw_engine; const DrawEngineDataSize *data_size = engine_type->vedata_size; @@ -103,7 +103,7 @@ static void draw_viewport_engines_data_clear(ViewportEngineData *data) MEM_SAFE_FREE(data->stl->storage[i]); } - if (data->instance_data) { + if (clear_instance_data && data->instance_data) { BLI_assert(engine_type->instance_free != nullptr); engine_type->instance_free(data->instance_data); data->instance_data = nullptr; @@ -120,7 +120,7 @@ static void draw_viewport_engines_data_clear(ViewportEngineData *data) } } -static void draw_view_data_clear(DRWViewData *view_data) +static void draw_view_data_clear(DRWViewData *view_data, bool free_instance_data) { GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.default_fb); GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.overlay_fb); @@ -137,23 +137,20 @@ static void draw_view_data_clear(DRWViewData *view_data) GPU_TEXTURE_FREE_SAFE(view_data->dtxl.depth_in_front); for (ViewportEngineData &engine : view_data->engines) { - draw_viewport_engines_data_clear(&engine); + draw_viewport_engines_data_clear(&engine, free_instance_data); } - - view_data->texture_list_size[0] = view_data->texture_list_size[1] = 0; - view_data->cache_time = 0.0f; } void DRW_view_data_free(DRWViewData *view_data) { - draw_view_data_clear(view_data); + draw_view_data_clear(view_data, true); delete view_data; } void DRW_view_data_texture_list_size_validate(DRWViewData *view_data, const int size[2]) { if (!equals_v2v2_int(view_data->texture_list_size, size)) { - draw_view_data_clear(view_data); + draw_view_data_clear(view_data, false); copy_v2_v2_int(view_data->texture_list_size, size); } } @@ -195,7 +192,7 @@ void DRW_view_data_free_unused(DRWViewData *view_data) { for (ViewportEngineData &engine : view_data->engines) { if (view_data->enabled_engines.first_index_of_try(&engine) == -1) { - draw_viewport_engines_data_clear(&engine); + draw_viewport_engines_data_clear(&engine, false); } } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc index 2a4a6a186be..f4c54b2f881 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc @@ -188,7 +188,7 @@ static void extract_vert_idx_init_subdiv(const DRWSubdivCache *subdiv_cache, { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom; - /* Each element points to an element in the ibo.points. */ + /* Each element points to an element in the `ibo.points`. */ draw_subdiv_init_origindex_buffer( vbo, (int32_t *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index), diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl index 0460ba56e4c..a8931292064 100644 --- a/source/blender/draw/intern/shaders/common_globals_lib.glsl +++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl @@ -110,7 +110,7 @@ layout(std140) uniform globalsBlock vec4 screenVecs[2]; vec4 sizeViewport; /* Inverted size in zw. */ - float sizePixel; /* This one is for dpi scaling */ + float sizePixel; /* This one is for DPI scaling. */ float pixelFac; /* To use with mul_project_m4_v3_zfac() */ float sizeObjectCenter; float sizeLightCenter; diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl index 2eccae5bceb..6c784469e5b 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl @@ -1,12 +1,7 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -/* To be compiled with common_hair_lib.glsl */ - +#ifdef USE_TF out vec4 finalColor; - -#ifdef TF_WORKAROUND -uniform int targetWidth; -uniform int targetHeight; -uniform int idOffset; #endif void main(void) diff --git a/source/blender/draw/intern/shaders/draw_hair_refine_info.hh b/source/blender/draw/intern/shaders/draw_hair_refine_info.hh index b80537f9deb..b477520bb97 100644 --- a/source/blender/draw/intern/shaders/draw_hair_refine_info.hh +++ b/source/blender/draw/intern/shaders/draw_hair_refine_info.hh @@ -24,3 +24,26 @@ GPU_SHADER_CREATE_INFO(draw_hair_refine_compute) .compute_source("common_hair_refine_comp.glsl") .define("HAIR_PHASE_SUBDIV") .do_static_compilation(true); + +GPU_SHADER_INTERFACE_INFO(draw_hair_refine_transform_feedback_workaround_iface, "") + .smooth(Type::VEC4, "finalColor"); + +GPU_SHADER_CREATE_INFO(draw_hair_refine_transform_feedback_workaround) + .define("srgbTarget", "false") + .define("blender_srgb_to_framebuffer_space(a)", "a") + .define("HAIR_PHASE_SUBDIV") + .define("TF_WORKAROUND") + + /* Move these to "draw_hair"? */ + .sampler(0, ImageType::UINT_BUFFER, "hairStrandBuffer") + .sampler(1, ImageType::UINT_BUFFER, "hairStrandSegBuffer") + + .push_constant(Type::INT, "targetWidth") + .push_constant(Type::INT, "targetHeight") + .push_constant(Type::INT, "idOffset") + .vertex_out(draw_hair_refine_transform_feedback_workaround_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .vertex_source("common_hair_refine_vert.glsl") + .fragment_source("gpu_shader_3D_smooth_color_frag.glsl") + .additional_info("draw_hair") + .do_static_compilation(true); diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 58d093c678d..786204a52ed 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -168,11 +168,11 @@ void draw_keyframe_shape(float x, /* Common attributes shared between the draw calls. */ typedef struct DrawKeylistUIData { float alpha; - float icon_sz; - float half_icon_sz; - float smaller_sz; - float ipo_sz; - float gpencil_sz; + float icon_size; + float half_icon_size; + float smaller_size; + float ipo_size; + float gpencil_size; float screenspace_margin; float sel_color[4]; float unsel_color[4]; @@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, /* TODO: allow this opacity factor to be themed? */ ctx->alpha = channel_locked ? 0.25f : 1.0f; - ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; - ctx->half_icon_sz = 0.5f * ctx->icon_sz; - ctx->smaller_sz = 0.35f * ctx->icon_sz; - ctx->ipo_sz = 0.1f * ctx->icon_sz; - ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->icon_size = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_size = 0.5f * ctx->icon_size; + ctx->smaller_size = 0.35f * ctx->icon_size; + ctx->ipo_size = 0.1f * ctx->icon_size; + ctx->gpencil_size = ctx->smaller_size * 0.8f; ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; @@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), - .ymin = ypos - ctx->gpencil_sz, - .ymax = ypos + ctx->gpencil_sz, + .ymin = ypos - ctx->gpencil_size, + .ymax = ypos + ctx->gpencil_size, }, true, 0.25f * (float)UI_UNIT_X, @@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->smaller_sz, - .ymax = ypos + ctx->smaller_sz, + .ymin = ypos - ctx->smaller_size, + .ymax = ypos + ctx->smaller_size, }, true, 3.0f, @@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->half_icon_sz, - .ymax = ypos + ctx->half_icon_sz, + .ymin = ypos - ctx->half_icon_size, + .ymax = ypos + ctx->half_icon_size, }, true, 3.0f, @@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->ipo_sz, - .ymax = ypos + ctx->ipo_sz, + .ymin = ypos - ctx->ipo_size, + .ymax = ypos + ctx->ipo_size, }, true, 3.0f, @@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx, draw_keyframe_shape(ak->cfra, ypos, - ctx->icon_sz, + ctx->icon_size, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index b21ae305dbe..791e28de694 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -23,9 +23,9 @@ set(SRC editcurve.c editcurve_add.c editcurve_paint.c + editcurve_pen.c editcurve_query.c editcurve_select.c - editcurve_pen.c editcurve_undo.c editfont.c editfont_undo.c diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 7d9e663c444..552ef1d96c8 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -20,7 +20,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi MutableSpan<float3> positions = curves.positions_for_write(); float *radius_data = (float *)CustomData_add_layer_named( - &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius"); MutableSpan<float> radii{radius_data, curves.points_num()}; for (const int i : offsets.index_range()) { diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc index 0667da46e25..dfc74f6d225 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.cc +++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc @@ -283,10 +283,6 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op } MEM_freeN(layer_name); - /* Layer Transform matrix. */ - float matrix[4][4]; - BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); - /* Apply time modifier. */ int remap_cfra = BKE_gpencil_time_modifier_cfra( depsgraph, scene, elem->ob, gpl_src, CFRA, false); @@ -324,7 +320,6 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op /* Update point location to new object space. */ for (int j = 0; j < gps->totpoints; j++) { bGPDspoint *pt = &gps->points[j]; - mul_m4_v3(matrix, &pt->x); mul_m4_v3(invmat, &pt->x); } diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index c367072e6e7..e9fcd2bd5fe 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -164,8 +164,16 @@ void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); +/** + * Return the File Browser area in which \a file_operator is active. + */ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win, const struct wmOperator *file_operator); +/** + * Check if there is any area in \a win that acts as a modal File Browser (#SpaceFile.op is set) + * and return it. + */ +struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win); /* TODO: Maybe we should move this to BLI? * On the other hand, it's using defines from space-file area, so not sure... */ diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4c01c75e06c..80a75da27f8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -24,6 +24,7 @@ struct Object; struct Scene; struct SpaceImage; struct ToolSettings; +struct View2D; struct ViewLayer; struct bContext; struct bNode; @@ -242,15 +243,12 @@ void uvedit_deselect_flush(const struct Scene *scene, struct BMEditMesh *em); */ void uvedit_select_flush(const struct Scene *scene, struct BMEditMesh *em); -bool ED_uvedit_nearest_uv(const struct Scene *scene, - struct Object *obedit, - const float co[2], - float *dist_sq, - float r_uv[2]); -bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, +bool ED_uvedit_nearest_uv_multi(const struct View2D *v2d, + const struct Scene *scene, struct Object **objects, uint objects_len, - const float co[2], + const int mval[2], + const bool ignore_selected, float *dist_sq, float r_uv[2]); @@ -268,6 +266,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene, int *r_verts_len); void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy); void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l); struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d1a6501408c..ea1095b26ff 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -164,7 +164,7 @@ DEF_ICON(NLA) DEF_ICON(PREFERENCES) DEF_ICON(TIME) DEF_ICON(NODETREE) -DEF_ICON_BLANK(181) +DEF_ICON(GEOMETRY_NODES) DEF_ICON(CONSOLE) DEF_ICON_BLANK(183) DEF_ICON(TRACKER) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 9f4d6815287..301171a284d 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -221,7 +221,7 @@ enum { UI_BUT_HAS_SEP_CHAR = 1 << 27, /** Don't run updates while dragging (needed in rare cases). */ UI_BUT_UPDATE_DELAY = 1 << 28, - /** When widget is in textedit mode, update value on each char stroke */ + /** When widget is in text-edit mode, update value on each char stroke. */ UI_BUT_TEXTEDIT_UPDATE = 1 << 29, /** Show 'x' icon to clear/unlink value of text or search button. */ UI_BUT_VALUE_CLEAR = 1 << 30, diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 6416421f4f5..a1a98a4b08c 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -92,7 +92,7 @@ void UI_icon_render_id_ex(const struct bContext *C, int UI_icon_preview_to_render_size(enum eIconSizes size); /** - * Draws icon with dpi scale factor. + * Draws icon with DPI scale factor. */ void UI_icon_draw(float x, float y, int icon_id); void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index c09ff68bbca..88e00cc5bec 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -147,7 +147,8 @@ struct uiBut { /** Pointer back to the layout item holding this button. */ uiLayout *layout; - int flag, drawflag; + uint64_t flag; + int drawflag; eButType type; eButPointerType pointype; short bit, bitnr, retval, strwidth, alignnr; diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc index 0156a943015..291ede05730 100644 --- a/source/blender/editors/interface/interface_style.cc +++ b/source/blender/editors/interface/interface_style.cc @@ -376,7 +376,7 @@ void uiStyleInit(void) { const uiStyle *style = static_cast<uiStyle *>(U.uistyles.first); - /* recover from uninitialized dpi */ + /* Recover from uninitialized DPI. */ if (U.dpi == 0) { U.dpi = 72; } diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.cc b/source/blender/editors/interface/view2d_gizmo_navigate.cc index 01729e35246..fae28833e4f 100644 --- a/source/blender/editors/interface/view2d_gizmo_navigate.cc +++ b/source/blender/editors/interface/view2d_gizmo_navigate.cc @@ -130,6 +130,13 @@ static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSE } break; } + case SPACE_IMAGE: { + const SpaceImage *sima = static_cast<const SpaceImage *>(area->spacedata.first); + if (sima->gizmo_flag & (SI_GIZMO_HIDE | SI_GIZMO_HIDE_NAVIGATE)) { + return false; + } + break; + } } return true; } diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 418a399db28..ef093a01ff8 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../../depsgraph ../../io/alembic ../../io/collada + ../../io/common ../../io/gpencil ../../io/usd ../../io/wavefront_obj diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 3345d422bd1..886586ff236 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -29,6 +29,7 @@ #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #include "IO_wavefront_obj.h" #include "io_obj.h" @@ -59,6 +60,15 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = { "Export objects as they appear in the viewport"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem io_obj_path_mode[] = { + {PATH_REFERENCE_AUTO, "AUTO", 0, "Auto", "Use Relative paths with subdirectories only"}, + {PATH_REFERENCE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Always write absolute paths"}, + {PATH_REFERENCE_RELATIVE, "RELATIVE", 0, "Relative", "Write relative paths where possible"}, + {PATH_REFERENCE_MATCH, "MATCH", 0, "Match", "Match Absolute/Relative setting with input path"}, + {PATH_REFERENCE_STRIP, "STRIP", 0, "Strip", "Write filename only"}, + {PATH_REFERENCE_COPY, "COPY", 0, "Copy", "Copy the file to the destination path"}, + {0, NULL, 0, NULL, NULL}}; + static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -87,6 +97,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } struct OBJExportParams export_params; + export_params.file_base_for_tests[0] = '\0'; RNA_string_get(op->ptr, "filepath", export_params.filepath); export_params.blen_filepath = CTX_data_main(C)->filepath; export_params.export_animation = RNA_boolean_get(op->ptr, "export_animation"); @@ -103,6 +114,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv"); export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals"); export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials"); + export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs"); @@ -119,9 +131,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) { - const bool export_animation = RNA_boolean_get(imfptr, "export_animation"); const bool export_smooth_groups = RNA_boolean_get(imfptr, "export_smooth_groups"); + const bool export_materials = RNA_boolean_get(imfptr, "export_materials"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -150,6 +162,9 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE); uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE); + sub = uiLayoutColumn(sub, false); + uiLayoutSetEnabled(sub, export_materials); + uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE); /* Options for what to write. */ box = uiLayoutBox(layout); @@ -162,6 +177,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); + /* Grouping options. */ box = uiLayoutBox(layout); uiItemL(box, IFACE_("Grouping"), ICON_GROUP); col = uiLayoutColumn(box, false); @@ -231,6 +247,8 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op) void WM_OT_obj_export(struct wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Export Wavefront OBJ"; ot->description = "Save the scene to a Wavefront OBJ file"; ot->idname = "WM_OT_obj_export"; @@ -244,7 +262,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) ot->flag |= OPTYPE_PRESET; WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, @@ -320,6 +338,12 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Materials", "Export MTL library. There must be a Principled-BSDF node for image textures to " "be exported to the MTL file"); + RNA_def_enum(ot->srna, + "path_mode", + io_obj_path_mode, + PATH_REFERENCE_AUTO, + "Path Mode", + "Method used to reference paths"); RNA_def_boolean(ot->srna, "export_triangulated_mesh", false, @@ -358,6 +382,10 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Every smooth-shaded face is assigned group \"1\" and every flat-shaded face \"off\""); RNA_def_boolean( ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", ""); + + /* Only show .obj or .mtl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -415,6 +443,8 @@ static void wm_obj_import_draw(bContext *C, wmOperator *op) void WM_OT_obj_import(struct wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Import Wavefront OBJ"; ot->description = "Load a Wavefront OBJ scene"; ot->idname = "WM_OT_obj_import"; @@ -425,7 +455,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) ot->ui = wm_obj_import_draw; WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, @@ -453,4 +483,8 @@ void WM_OT_obj_import(struct wmOperatorType *ot) false, "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + + /* Only show .obj or .mtl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index e0616a0cec3..97ca9b026ec 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -118,7 +118,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface"); const bool export_textures = RNA_boolean_get(op->ptr, "export_textures"); const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures"); - const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths"); + const bool relative_paths = RNA_boolean_get(op->ptr, "relative_paths"); struct USDExportParams params = { export_animation, @@ -133,7 +133,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) generate_preview_surface, export_textures, overwrite_textures, - relative_texture_paths, + relative_paths, }; bool ok = USD_export(C, filename, ¶ms, as_background_job); @@ -181,9 +181,9 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) const bool export_tex = RNA_boolean_get(ptr, "export_textures"); uiLayoutSetActive(row, export_mtl && preview && export_tex); - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE); - uiLayoutSetActive(row, export_mtl && preview); + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("File References")); + uiItemR(col, ptr, "relative_paths", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); uiItemL(box, IFACE_("Experimental"), ICON_NONE); @@ -282,10 +282,11 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "Allow overwriting existing texture files when exporting textures"); RNA_def_boolean(ot->srna, - "relative_texture_paths", + "relative_paths", true, - "Relative Texture Paths", - "Make texture asset paths relative to the USD file"); + "Relative Paths", + "Use relative paths to reference external files (i.e. textures, volumes) in " + "USD, otherwise use absolute paths"); } /* ====== USD Import ====== */ diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 2642a613e92..166eb40a7db 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -99,8 +99,8 @@ static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_selec BM_edge_select_set(em->bm, e, true); } } + EDBM_selectmode_flush(em); } - EDBM_select_flush(em); } EDBM_update(me, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index ee40431c101..5d8cf176b87 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -190,6 +190,22 @@ typedef struct KnifeBVH { } KnifeBVH; +/** Additional per-object data. */ +typedef struct KnifeObjectInfo { + const float (*cagecos)[3]; + + /** + * Optionally allocate triangle indices, these are needed for non-interactive knife + * projection as multiple cuts are made without the BVH being updated. + * Using these indices the it's possible to access `cagecos` even if the face has been cut + * and the loops in `em->looptris` no longer refer to the original triangles, see: + */ + const int (*tri_indices)[3]; + + /** Only assigned for convenient access. */ + BMEditMesh *em; +} KnifeObjectInfo; + /* struct for properties used while drawing */ typedef struct KnifeTool_OpData { ARegion *region; /* Region that knifetool was activated in. */ @@ -203,6 +219,9 @@ typedef struct KnifeTool_OpData { Object **objects; uint objects_len; + /** Array `objects_len` length of additional per-object data. */ + KnifeObjectInfo *objects_info; + MemArena *arena; /* Reused for edge-net filling. */ @@ -220,7 +239,6 @@ typedef struct KnifeTool_OpData { GHash *facetrimap; KnifeBVH bvh; - const float (**cagecos)[3]; BLI_mempool *kverts; BLI_mempool *kedges; @@ -1134,6 +1152,52 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k /** \} */ /* -------------------------------------------------------------------- */ +/** \name Knife Object Info Accessors (#KnifeObjectInfo) + * \{ */ + +static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + int tri_index_buf[3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + if (obinfo->tri_indices) { + return obinfo->tri_indices[tri_index]; + } + for (int i = 0; i < 3; i++) { + tri_index_buf[i] = BM_elem_index_get(obinfo->em->looptris[tri_index][i]->v); + } + return tri_index_buf; +} + +static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + int tri_ind_buf[3]; + const int *tri_ind = knife_bm_tri_index_get(kcd, base_index, tri_index, tri_ind_buf); + for (int i = 0; i < 3; i++) { + copy_v3_v3(cos[i], obinfo->cagecos[tri_ind[i]]); + } +} + +static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + knife_bm_tri_cagecos_get(kcd, base_index, tri_index, cos); + const Object *ob = kcd->objects[base_index]; + for (int i = 0; i < 3; i++) { + mul_m4_v3(ob->obmat, cos[i]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Knife BVH Utils * \{ */ @@ -1219,16 +1283,7 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) if (!test_fn_ret) { continue; } - - copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]); - copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]); - copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]); - - /* Convert to world-space. */ - mul_m4_v3(ob->obmat, cos[0]); - mul_m4_v3(ob->obmat, cos[1]); - mul_m4_v3(ob->obmat, cos[2]); - + knife_bm_tri_cagecos_get_worldspace(kcd, b, i, cos); BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); } @@ -1282,12 +1337,7 @@ static void knife_bvh_raycast_cb(void *userdata, } } - copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]); - copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]); - copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]); - mul_m4_v3(ob->obmat, tri_cos[0]); - mul_m4_v3(ob->obmat, tri_cos[1]); - mul_m4_v3(ob->obmat, tri_cos[2]); + knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); isect = (ray->radius > 0.0f ? @@ -1684,7 +1734,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->cagecos[base_index][BM_elem_index_get(v)]; + cageco = kcd->objects_info[base_index].cagecos[BM_elem_index_get(v)]; } else { cageco = v->co; @@ -2545,12 +2595,8 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, if (tri[0]->f != f) { break; } - copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]); - copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]); - copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]); - mul_m4_v3(ob->obmat, lv[0]); - mul_m4_v3(ob->obmat, lv[1]); - mul_m4_v3(ob->obmat, lv[2]); + + knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, lv); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with @@ -2605,9 +2651,10 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd) ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - if (kcd->cagecos[b]) { + const float(*cagecos)[3] = kcd->objects_info[b].cagecos; + if (cagecos) { for (int i = 0; i < em->bm->totvert; i++) { - copy_v3_v3(ws, kcd->cagecos[b][i]); + copy_v3_v3(ws, cagecos[i]); mul_m4_v3(ob->obmat, ws); minmax_v3v3_v3(min, max, ws); } @@ -3961,10 +4008,13 @@ static void knifetool_undo(KnifeTool_OpData *kcd) /** \} */ /* -------------------------------------------------------------------- */ -/** \name #KnifeTool_OpData (#op->customdata) Init and Free +/** \name #KnifeObjectInfo (#kcd->objects_info) Init and Free * \{ */ -static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index) +static void knifetool_init_obinfo(KnifeTool_OpData *kcd, + Object *ob, + uint base_index, + bool use_tri_indices) { Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); @@ -3973,18 +4023,36 @@ static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_ BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc( + KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + obinfo->em = em_eval; + obinfo->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); + + if (use_tri_indices) { + BMLoop *(*looptris)[3] = em_eval->looptris; + int(*tri_indices)[3] = MEM_mallocN(sizeof(int[3]) * em_eval->tottri, __func__); + for (int i = 0; i < em_eval->tottri; i++) { + BMLoop **tri = looptris[i]; + tri_indices[i][0] = BM_elem_index_get(tri[0]->v); + tri_indices[i][1] = BM_elem_index_get(tri[1]->v); + tri_indices[i][2] = BM_elem_index_get(tri[2]->v); + } + obinfo->tri_indices = tri_indices; + } } -static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index) +static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint base_index) { - if (kcd->cagecos[base_index]) { - MEM_freeN((void *)kcd->cagecos[base_index]); - kcd->cagecos[base_index] = NULL; - } + MEM_SAFE_FREE(kcd->objects_info[base_index].cagecos); + MEM_SAFE_FREE(kcd->objects_info[base_index].tri_indices); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #KnifeTool_OpData (#op->customdata) Init and Free + * \{ */ + static void knife_init_colors(KnifeColors *colors) { /* Possible BMESH_TODO: add explicit themes or calculate these by @@ -4018,6 +4086,10 @@ static void knifetool_init(bContext *C, const float angle_snapping_increment, const bool is_interactive) { + /* Needed so multiple non-interactive cuts (also called knife-project) + * doesn't access indices of loops that were created by cutting, see: T97153. */ + bool use_tri_indices = !is_interactive; + kcd->vc = *vc; Scene *scene = vc->scene; @@ -4031,11 +4103,11 @@ static void knifetool_init(bContext *C, Object *ob; BMEditMesh *em; - kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos"); + kcd->objects_info = MEM_callocN(sizeof(*kcd->objects_info) * kcd->objects_len, "knife cagecos"); for (uint b = 0; b < kcd->objects_len; b++) { ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - knifetool_init_cagecos(kcd, ob, b); + knifetool_init_obinfo(kcd, ob, b, use_tri_indices); /* Can't usefully select resulting edges in face mode. */ kcd->select_result = (em->selectmode != SCE_SELECT_FACE); @@ -4139,9 +4211,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) /* Knife BVH cleanup. */ for (int i = 0; i < kcd->objects_len; i++) { - knifetool_free_cagecos(kcd, i); + knifetool_free_obinfo(kcd, i); } - MEM_freeN((void *)kcd->cagecos); + MEM_freeN((void *)kcd->objects_info); knife_bvh_free(kcd); /* Line-hits cleanup. */ @@ -4391,7 +4463,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; - case KNF_MODAL_CONFIRM: + case KNF_MODAL_CONFIRM: { + const bool changed = (kcd->totkvert != 0); /* finish */ ED_region_tag_redraw(kcd->region); @@ -4400,11 +4473,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); /* Cancel to prevent undo push for empty cuts. */ - if (kcd->totkvert == 0) { + if (!changed) { return OPERATOR_CANCELLED; } - return OPERATOR_FINISHED; + } case KNF_MODAL_UNDO: if (BLI_stack_is_empty(kcd->undostack)) { ED_region_tag_redraw(kcd->region); diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 5b857d2dba1..b7deade5146 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -2116,6 +2116,22 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool object_curves_empty_hair_add_poll(bContext *C) +{ + if (!U.experimental.use_new_curves_type) { + return false; + } + if (!ED_operator_objectmode(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + if (ob == nullptr || ob->type != OB_MESH) { + CTX_wm_operator_poll_msg_set(C, "No active mesh object"); + return false; + } + return true; +} + void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) { ot->name = "Add Empty Curves"; @@ -2123,7 +2139,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_curves_empty_hair_add"; ot->exec = object_curves_empty_hair_add_exec; - ot->poll = object_curves_add_poll; + ot->poll = object_curves_empty_hair_add_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2762,9 +2778,32 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif + {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"}, {0, nullptr, 0, nullptr, nullptr}, }; +static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *items = nullptr; + int items_num = 0; + for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) { + if (item->value == OB_CURVES) { + if (U.experimental.use_new_curves_type) { + RNA_enum_item_add(&items, &items_num, item); + } + } + else { + RNA_enum_item_add(&items, &items_num, item); + } + } + RNA_enum_item_end(&items, &items_num); + *r_free = true; + return items; +} + static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == nullptr) { @@ -2894,6 +2933,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) Object *ob1, *obact = CTX_data_active_object(C); const short target = RNA_enum_get(op->ptr, "target"); bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); + const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata"); const float angle = RNA_float_get(op->ptr, "angle"); const int thickness = RNA_int_get(op->ptr, "thickness"); @@ -3064,6 +3104,50 @@ static int object_convert_exec(bContext *C, wmOperator *op) } ob_gpencil->actcol = actcol; } + else if (target == OB_CURVES) { + ob->flag |= OB_DONE; + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + GeometrySet geometry; + if (ob_eval->runtime.geometry_set_eval != nullptr) { + geometry = *ob_eval->runtime.geometry_set_eval; + } + + if (geometry.has_curves()) { + if (keep_original) { + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); + newob = basen->object; + + /* Decrement original curve's usage count. */ + Curve *legacy_curve = static_cast<Curve *>(newob->data); + id_us_min(&legacy_curve->id); + + /* Make a copy of the curve. */ + newob->data = BKE_id_copy(bmain, &legacy_curve->id); + } + else { + newob = ob; + } + + const CurveComponent &curve_component = *geometry.get_component_for_read<CurveComponent>(); + const Curves *curves_eval = curve_component.get_for_read(); + Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2)); + + newob->data = new_curves; + newob->type = OB_CURVES; + + blender::bke::CurvesGeometry::wrap( + new_curves->geometry) = blender::bke::CurvesGeometry::wrap(curves_eval->geometry); + BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id); + + BKE_object_free_derived_caches(newob); + BKE_object_free_modifiers(newob, 0); + } + else { + BKE_reportf( + op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2); + } + } else if (ob->type == OB_MESH && target == OB_POINTCLOUD) { ob->flag |= OB_DONE; @@ -3122,6 +3206,10 @@ static int object_convert_exec(bContext *C, wmOperator *op) Mesh *new_mesh = (Mesh *)newob->data; BKE_mesh_nomain_to_mesh(me_eval, new_mesh, newob, &CD_MASK_MESH, true); + if (do_merge_customdata) { + BKE_mesh_merge_customdata_for_apply_modifier(new_mesh); + } + /* Anonymous attributes shouldn't be available on the applied geometry. */ MeshComponent component; component.replace(new_mesh, GeometryOwnershipType::Editable); @@ -3441,7 +3529,11 @@ static void object_convert_ui(bContext *UNUSED(C), wmOperator *op) uiItemR(layout, op->ptr, "target", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "keep_original", 0, nullptr, ICON_NONE); - if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) { + const int target = RNA_enum_get(op->ptr, "target"); + if (target == OB_MESH) { + uiItemR(layout, op->ptr, "merge_customdata", 0, nullptr, ICON_NONE); + } + else if (target == OB_GPENCIL) { uiItemR(layout, op->ptr, "thickness", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "angle", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "offset", 0, nullptr, ICON_NONE); @@ -3471,12 +3563,20 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); + RNA_def_enum_funcs(ot->prop, convert_target_items_fn); RNA_def_boolean(ot->srna, "keep_original", false, "Keep Original", "Keep original objects instead of replacing them"); + RNA_def_boolean( + ot->srna, + "merge_customdata", + true, + "Merge UV's", + "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); + prop = RNA_def_float_rotation(ot->srna, "angle", 0, diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 2e878770347..d982d86fe77 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1715,7 +1715,7 @@ static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) Object *prev_ob = NULL; - /* Copy all constraints from active posebone to all selected posebones. */ + /* Copy all constraints from active pose-bone to all selected pose-bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { /* If we're not handling the object we're copying from, copy all constraints over. */ if (pchan == chan) { @@ -2115,7 +2115,7 @@ static int pose_constraint_copy_exec(bContext *C, wmOperator *op) Object *prev_ob = NULL; - /* copy all constraints from active posebone to all selected posebones */ + /* Copy all constraints from active pose-bone to all selected pose-bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { /* if we're not handling the object we're copying from, copy all constraints over */ if (pchan != chan) { diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index f90a31a7cbe..eed0a63565e 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -740,7 +740,13 @@ static bool modifier_apply_obdata( } else { Mesh *mesh_applied = modifier_apply_create_mesh_for_modifier( - depsgraph, ob, md_eval, true, true); + depsgraph, + ob, + md_eval, + /* It's important not to apply virtual modifiers (e.g. shape-keys) because they're kept, + * causing them to be applied twice, see: T97758. */ + false, + true); if (!mesh_applied) { BKE_report(reports, RPT_ERROR, "Modifier returned error, skipping apply"); return false; @@ -1434,8 +1440,10 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); const bool do_report = RNA_boolean_get(op->ptr, "report"); const bool do_single_user = RNA_boolean_get(op->ptr, "single_user"); + const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata"); if (md == nullptr) { return OPERATOR_CANCELLED; @@ -1460,6 +1468,11 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo return OPERATOR_CANCELLED; } + if (do_merge_customdata && + (mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) { + BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data); + } + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1518,6 +1531,12 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot) edit_modifier_properties(ot); edit_modifier_report_property(ot); + RNA_def_boolean( + ot->srna, + "merge_customdata", + true, + "Merge UV's", + "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); PropertyRNA *prop = RNA_def_boolean(ot->srna, "single_user", false, @@ -1599,11 +1618,18 @@ static int modifier_convert_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); + const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata"); if (!md || !ED_object_modifier_convert(op->reports, bmain, depsgraph, view_layer, ob, md)) { return OPERATOR_CANCELLED; } + if (do_merge_customdata && + (mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) { + BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data); + } + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1631,6 +1657,13 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); + + RNA_def_boolean( + ot->srna, + "merge_customdata", + true, + "Merge UV's", + "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 5fcb1d2850d..feda57fff1f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -18,9 +18,11 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_spline.hh" #include "DNA_brush_enums.h" @@ -104,10 +106,11 @@ struct AddOperationExecutor { bool use_front_face_; bool interpolate_length_; bool interpolate_shape_; + bool interpolate_point_count_; bool use_interpolation_; float new_curve_length_; int add_amount_; - int points_per_curve_ = 8; + int constant_points_per_curve_; /** Various matrices to convert between coordinate spaces. */ float4x4 curves_to_world_mat_; @@ -128,6 +131,15 @@ struct AddOperationExecutor { Vector<int> looptri_indices; }; + struct NeighborInfo { + /* Curve index of the neighbor. */ + int index; + /* The weights of all neighbors of a new curve add up to 1. */ + float weight; + }; + static constexpr int max_neighbors = 5; + using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) { self_ = &self; @@ -171,9 +183,12 @@ struct AddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); + constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - use_interpolation_ = interpolate_length_ || interpolate_shape_; + interpolate_point_count_ = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + use_interpolation_ = interpolate_length_ || interpolate_shape_ || interpolate_point_count_; new_curve_length_ = brush_settings_->curve_length; tot_old_curves_ = curves_->curves_num(); @@ -213,18 +228,28 @@ struct AddOperationExecutor { return; } + Array<NeighborsVector> neighbors_per_curve; if (use_interpolation_) { this->ensure_curve_roots_kdtree(); + neighbors_per_curve = this->find_curve_neighbors(added_points); } + /* Resize to add the new curves, building the offsets in the array owned by the curves. */ const int tot_added_curves = added_points.bary_coords.size(); - const int tot_added_points = tot_added_curves * points_per_curve_; + curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); + if (interpolate_point_count_) { + this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); + } + else { + this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); + } + + /* Resize to add the correct point count calculated as part of building the offsets. */ + curves_->resize(curves_->offsets().last(), curves_->curves_num()); - curves_->resize(curves_->points_num() + tot_added_points, - curves_->curves_num() + tot_added_curves); + this->initialize_attributes(added_points, neighbors_per_curve); - threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, - [&]() { this->initialize_attributes(added_points); }); + curves_->update_curve_types(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); ED_region_tag_redraw(region_); @@ -576,33 +601,37 @@ struct AddOperationExecutor { } } - void initialize_curve_offsets(const int tot_added_curves) + void initialize_curve_offsets_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) { - MutableSpan<int> offsets = curves_->offsets_for_write(); - threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + + attribute_math::DefaultMixer<int> mixer{new_offsets}; + threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { + for (const int i : curves_range) { + for (const NeighborInfo &neighbor : neighbors_per_curve[i]) { + const int neighbor_points_num = curves_->points_for_curve(neighbor.index).size(); + mixer.mix_in(i, neighbor_points_num, neighbor.weight); + } } }); - } + mixer.finalize(); - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); + } - void initialize_attributes(const AddedPoints &added_points) + void initialize_curve_offsets_without_interpolation(const int points_per_curve) { - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - neighbors_per_curve = this->find_curve_neighbors(added_points); + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + int offset = tot_old_points_; + for (const int i : new_offsets.index_range()) { + new_offsets[i] = offset; + offset += points_per_curve; } + } + void initialize_attributes(const AddedPoints &added_points, + const Span<NeighborsVector> neighbors_per_curve) + { Array<float> new_lengths_cu(added_points.bary_coords.size()); if (interpolate_length_) { this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); @@ -731,15 +760,14 @@ struct AddOperationExecutor { threading::parallel_for( added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { - const int first_point_i = tot_old_points_ + i * points_per_curve_; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float3 &root_cu = added_points.positions_cu[i]; const float length = lengths_cu[i]; const float3 &normal_su = normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 tip_cu = root_cu + length * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); } }); } @@ -760,23 +788,22 @@ struct AddOperationExecutor { added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float length_cu = new_lengths_cu[i]; const float3 &normal_su = new_normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 &root_cu = added_points.positions_cu[i]; - const int first_point_i = tot_old_points_ + i * points_per_curve_; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); continue; } - positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); + positions_cu.slice(points).fill(root_cu); for (const NeighborInfo &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; @@ -810,8 +837,8 @@ struct AddOperationExecutor { const float neighbor_length_cu = neighbor_spline.length(); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; - for (const int j : IndexRange(points_per_curve_)) { + const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; + for (const int j : IndexRange(points.size())) { const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( j * resample_factor); const float index_factor = lookup.evaluated_index + lookup.factor; @@ -821,7 +848,7 @@ struct AddOperationExecutor { const float3 relative_coord = p - neighbor_root_cu; float3 rotated_relative_coord = relative_coord; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; + positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; } } } @@ -835,8 +862,16 @@ void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports) { + Object &ob_active = *CTX_data_active_object(&C); + BLI_assert(ob_active.type == OB_CURVES); + Curves &curves_id = *static_cast<Curves *>(ob_active.data); + if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { + BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); + return {}; + } + return std::make_unique<AddOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index b5835abecbd..9d000649d62 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -33,7 +33,7 @@ class CurvesSculptStrokeOperation { virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 992ac77803a..d8713c8eb1d 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -74,12 +74,12 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ -static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, - wmOperator *op) +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, + wmOperator &op) { - const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op->ptr, "mode")); + const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode")); - Scene &scene = *CTX_data_scene(C); + Scene &scene = *CTX_data_scene(&C); CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); switch (brush.curves_sculpt_tool) { @@ -90,9 +90,9 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte case CURVES_SCULPT_TOOL_SNAKE_HOOK: return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: - return new_add_operation(); + return new_add_operation(C, op.reports); case CURVES_SCULPT_TOOL_GROW_SHRINK: - return new_grow_shrink_operation(mode, C); + return new_grow_shrink_operation(mode, &C); } BLI_assert_unreachable(); return {}; @@ -131,7 +131,7 @@ static void stroke_update_step(bContext *C, if (!op_data->operation) { stroke_extension.is_first = true; - op_data->operation = start_brush_operation(C, op); + op_data->operation = start_brush_operation(*C, *op); } else { stroke_extension.is_first = false; @@ -149,6 +149,12 @@ static void stroke_done(const bContext *C, PaintStroke *stroke) static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + if (brush == nullptr) { + return OPERATOR_CANCELLED; + } + SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__); op_data->stroke = paint_stroke_new(C, op, diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 1303da71435..02e992029ff 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -7,6 +7,7 @@ */ #include <float.h> +#include <limits.h> #include <math.h> #include <stdio.h> #include <string.h> @@ -35,12 +36,17 @@ #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_defs.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" +#include "DNA_object_enums.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_camera.h" #include "BKE_colorband.h" @@ -61,6 +67,8 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -76,12 +84,18 @@ #include "GPU_capabilities.h" #include "GPU_init_exit.h" +#include "NOD_shader.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "IMB_colormanagement.h" @@ -6459,6 +6473,38 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) return ima; } +static CustomDataLayer *proj_paint_color_attribute_create(wmOperator *op, Object *ob) +{ + char name[MAX_NAME] = ""; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + AttributeDomain domain = ATTR_DOMAIN_POINT; + CustomDataType type = CD_PROP_COLOR; + + if (op) { + RNA_string_get(op->ptr, "name", name); + RNA_float_get_array(op->ptr, "color", color); + domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); + } + + ID *id = (ID *)ob->data; + CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); + + if (!layer) { + return NULL; + } + + BKE_id_attributes_active_color_set(id, layer); + + if (!BKE_id_attributes_render_color_get(id)) { + BKE_id_attributes_render_color_set(id, layer); + } + + BKE_object_attributes_active_color_fill(ob, color, false); + + return layer; +} + static void proj_paint_default_color(wmOperator *op, int type, Material *ma) { if (RNA_struct_property_is_set(op->ptr, "color")) { @@ -6516,6 +6562,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Material *ma; Image *ima = NULL; + CustomDataLayer *layer = NULL; if (!ob) { return false; @@ -6528,7 +6575,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) int type = RNA_enum_get(op->ptr, "type"); bool is_data = (type > LAYER_BASE_COLOR); - bNode *imanode; + bNode *new_node; bNodeTree *ntree = ma->nodetree; if (!ntree) { @@ -6538,17 +6585,36 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - - ima = proj_paint_image_create(op, bmain, is_data); - imanode->id = &ima->id; - - nodeSetActive(ntree, imanode); + const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ? + (ePaintCanvasSource)RNA_enum_get(op->ptr, + "slot_type") : + PAINT_CANVAS_SOURCE_IMAGE; + + /* Create a new node. */ + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + ima = proj_paint_image_create(op, bmain, is_data); + new_node->id = &ima->id; + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_ATTRIBUTE); + if ((layer = proj_paint_color_attribute_create(op, ob))) { + BLI_strncpy_utf8( + ((NodeShaderAttribute *)new_node->storage)->name, layer->name, MAX_NAME); + } + break; + } + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + return false; + } + nodeSetActive(ntree, new_node); /* Connect to first available principled BSDF node. */ bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + bNode *out_node = new_node; if (in_node != NULL) { bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); @@ -6611,6 +6677,11 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); } + if (layer) { + BKE_texpaint_slot_refresh_cache(scene, ma, ob); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); + } DEG_id_tag_update(&ntree->id, 0); DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); @@ -6678,13 +6749,51 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, int type = get_texture_layer_type(op, "type"); proj_paint_default_color(op, type, ma); - char imagename[MAX_ID_NAME - 2]; - get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); - RNA_string_set(op->ptr, "name", imagename); + char name[MAX_NAME]; + get_default_texture_layer_name_for_object(ob, type, (char *)&name, sizeof(name)); + RNA_string_set(op->ptr, "name", name); return WM_operator_props_dialog_popup(C, op, 300); } +static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + Object *ob = ED_object_active_context(C); + ePaintCanvasSource slot_type = PAINT_CANVAS_SOURCE_IMAGE; + + if (ob->mode == OB_MODE_SCULPT) { + slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type"); + uiItemR(layout, op->ptr, "slot_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + } + + uiItemR(layout, op->ptr, "name", 0, NULL, ICON_NONE); + + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + + uiItemR(layout, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "float", 0, NULL, ICON_NONE); + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + uiItemR(layout, op->ptr, "domain", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "data_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + break; + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + break; + } + + uiItemR(layout, op->ptr, "color", 0, NULL, ICON_NONE); +} + #define IMA_DEF_NAME N_("Untitled") void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) @@ -6692,40 +6801,92 @@ void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) PropertyRNA *prop; static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + static const EnumPropertyItem slot_type_items[3] = { + {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""}, + {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem domain_items[3] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem attribute_type_items[3] = { + {CD_PROP_COLOR, "COLOR", 0, "Color", ""}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; + ot->name = "Add Paint Slot"; + ot->description = "Add a paint slot"; ot->idname = "PAINT_OT_add_texture_paint_slot"; /* api callbacks */ ot->invoke = texture_paint_add_texture_paint_slot_invoke; ot->exec = texture_paint_add_texture_paint_slot_exec; ot->poll = ED_operator_object_active_editable_mesh; + ot->ui = texture_paint_add_texture_paint_slot_ui; /* flags */ ot->flag = OPTYPE_UNDO; - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + /* Shared Properties */ + prop = RNA_def_enum(ot->srna, + "type", + layer_type_items, + 0, + "Material Layer Type", + "Material layer type of new paint slot"); RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_enum( + ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot"); + + prop = RNA_def_string( + ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_color( ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); + + /* Image Properties */ + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, "Generated Type", "Fill the image with a grid for UV map testing"); + RNA_def_boolean( ot->srna, "float", 0, "32-bit Float", "Create image with 32-bit floating-point bit depth"); + + /* Color Attribute Properties */ + RNA_def_enum(ot->srna, + "domain", + domain_items, + ATTR_DOMAIN_POINT, + "Domain", + "Type of element that attribute is stored on"); + + RNA_def_enum(ot->srna, + "data_type", + attribute_type_items, + CD_PROP_COLOR, + "Data Type", + "Type of data stored in attribute"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 32b7047c2b0..85ea5d5bfc6 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -5281,7 +5281,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f * canvas it is painting on. (ref. use_sculpt_texture_paint). */ if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { View3D *v3d = CTX_wm_view3d(C); - if (v3d) { + if (v3d->shading.type == OB_SOLID) { v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 09f13ff110d..f71a814aff4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -124,8 +124,8 @@ static void color_filter_task_cb(void *__restrict userdata, } case COLOR_FILTER_HUE: rgb_to_hsv_v(orig_color, hsv_color); - hue = hsv_color[0] + fade; - hsv_color[0] = fabs((hsv_color[0] + fade) - hue); + hue = hsv_color[0]; + hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue, 1); hsv_to_rgb_v(hsv_color, final_color); break; case COLOR_FILTER_SATURATION: @@ -328,8 +328,12 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent { Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + View3D *v3d = CTX_wm_view3d(C); SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; + if (v3d->shading.type == OB_SOLID) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL); if (use_automasking) { diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 2b5a20205bd..5aa1dbef74c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -1043,6 +1043,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; + View3D *v3d = CTX_wm_view3d(C); + if (v3d->shading.type == OB_SOLID) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index a63ed142ed8..2a8a2be8b65 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -761,7 +761,8 @@ static int sound_pack_exec(bContext *C, wmOperator *op) sound->packedfile = BKE_packedfile_new( op->reports, sound->filepath, ID_BLEND_PATH(bmain, &sound->id)); - BKE_sound_load(bmain, sound); + + DEG_id_tag_update_ex(bmain, &sound->id, ID_RECALC_AUDIO); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 9f71d6f77c7..59ecef7d4c6 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -2802,7 +2802,8 @@ int ED_path_extension_type(const char *path) if (BLI_path_extension_check(path, ".zip")) { return FILE_TYPE_ARCHIVE; } - if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) { + if (BLI_path_extension_check_n( + path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) { return FILE_TYPE_OBJECT_IO; } if (BLI_path_extension_check_array(path, imb_ext_image)) { diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 011506368ee..ce36e3e4e4f 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1364,3 +1364,21 @@ ScrArea *ED_fileselect_handler_area_find(const wmWindow *win, const wmOperator * return NULL; } + +ScrArea *ED_fileselect_handler_area_find_any_with_op(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + + ED_screen_areas_iter (win, screen, area) { + if (area->spacetype != SPACE_FILE) { + continue; + } + + const SpaceFile *sfile = area->spacedata.first; + if (sfile->op) { + return area; + } + } + + return NULL; +} diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 8b059b33a9a..40082b08806 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -58,14 +58,12 @@ * --> Most channels are now selection only. */ -static int mouse_nla_channels( - bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - View2D *v2d = &ac->region->v2d; int notifierFlags = 0; /* get the channel that was clicked on */ @@ -203,47 +201,8 @@ static int mouse_nla_channels( } case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)ale->data; - AnimData *adt = ale->adt; - short offset; - - /* offset for start of channel (on LHS of channel-list) */ - if (ale->id) { - /* special exception for materials and particles */ - if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) { - offset = 21 + NLACHANNEL_BUTTON_WIDTH; - } - else { - offset = 14; - } - } - else { - offset = 0; - } - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - /* toggle protection (only if there's a toggle there) */ - nlt->flag ^= NLATRACK_PROTECTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - } - else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) { - /* toggle mute */ - nlt->flag ^= NLATRACK_MUTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) { - /* toggle 'solo' */ - BKE_nlatrack_solo_toggle(adt, nlt); - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (nlaedit_is_tweakmode_on(ac) == 0) { + if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this F-Curve only */ @@ -269,61 +228,40 @@ static int mouse_nla_channels( case ANIMTYPE_NLAACTION: { AnimData *adt = BKE_animdata_from_id(ale->id); - /* button region... */ - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - if (nlaedit_is_tweakmode_on(ac) == 0) { - /* 'push-down' action - only usable when not in tweak-mode */ - /* TODO: make this use the operator instead of calling the function directly - * however, calling the operator requires that we supply the args, - * and that works with proper buttons only */ - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id)); - } - else { - /* When in tweak-mode, this button becomes the toggle for mapped editing. */ - adt->flag ^= ADT_NLA_EDIT_NOMAP; - } + /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block + * - this is useful when there's no clear divider, and makes more sense in + * the case of users trying to use this to change actions + * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection + * while in tweak-mode is really evil! + * - we disable "solo" flags too, to make it easier to work with stashed actions + * with less trouble + */ + if (nlaedit_is_tweakmode_on(ac)) { + /* Exit tweak-mode immediately. */ + nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; ale->update |= ANIM_UPDATE_DEPS; } - /* OR rest of name... */ else { - /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block - * - this is useful when there's no clear divider, and makes more sense in - * the case of users trying to use this to change actions - * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection - * while in tweak-mode is really evil! - * - we disable "solo" flags too, to make it easier to work with stashed actions - * with less trouble - */ - if (nlaedit_is_tweakmode_on(ac)) { - /* Exit tweak-mode immediately. */ - nlaedit_disable_tweakmode(ac, true); - - /* changes to NLA-Action occurred */ - notifierFlags |= ND_NLA_ACTCHANGE; - ale->update |= ANIM_UPDATE_DEPS; + /* select/deselect */ + if (selectmode == SELECT_INVERT) { + /* inverse selection status of this AnimData block only */ + adt->flag ^= ADT_UI_SELECTED; } else { - /* select/deselect */ - if (selectmode == SELECT_INVERT) { - /* inverse selection status of this AnimData block only */ - adt->flag ^= ADT_UI_SELECTED; - } - else { - /* select AnimData block by itself */ - ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); - adt->flag |= ADT_UI_SELECTED; - } - - /* set active? */ - if (adt->flag & ADT_UI_SELECTED) { - adt->flag |= ADT_UI_ACTIVE; - } + /* select AnimData block by itself */ + ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); + adt->flag |= ADT_UI_SELECTED; + } - notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); + /* set active? */ + if (adt->flag & ADT_UI_SELECTED) { + adt->flag |= ADT_UI_ACTIVE; } + + notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } @@ -386,7 +324,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv &channel_index); /* handle mouse-click in the relevant channel then */ - notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode); + notifierFlags = mouse_nla_channels(C, &ac, channel_index, selectmode); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 958a9fdfc60..d5507619e0d 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -213,6 +213,11 @@ static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } +static void node_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + NodeResizeDirection node_get_resize_direction(const bNode *node, const int x, const int y) { if (node->type == NODE_FRAME) { @@ -480,6 +485,10 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_MATH: ntype->draw_buttons = node_buts_math; break; + case SH_NODE_COMBINE_COLOR: + case SH_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_buts_combsep_color; + break; case SH_NODE_TEX_IMAGE: ntype->draw_buttons = node_shader_buts_tex_image; ntype->draw_buttons_ex = node_shader_buts_tex_image_ex; @@ -589,6 +598,19 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_composit_buts_combsep_color(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); + if (storage->mode == CMP_NODE_COMBSEP_COLOR_YCC) { + uiItemR(layout, ptr, "ycc_mode", DEFAULT_FLAGS, "", ICON_NONE); + } +} + static void node_composit_backdrop_viewer( SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) { @@ -821,8 +843,12 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_HUECORRECT: ntype->draw_buttons = node_composit_buts_huecorrect; break; - case CMP_NODE_COMBYCCA: - case CMP_NODE_SEPYCCA: + case CMP_NODE_COMBINE_COLOR: + case CMP_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_composit_buts_combsep_color; + break; + case CMP_NODE_COMBYCCA_LEGACY: + case CMP_NODE_SEPYCCA_LEGACY: ntype->draw_buttons = node_composit_buts_ycc; break; case CMP_NODE_MASK_BOX: @@ -975,6 +1001,11 @@ static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), Poin uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_texture_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + /* only once called */ static void node_texture_set_butfunc(bNodeType *ntype) { @@ -1020,6 +1051,11 @@ static void node_texture_set_butfunc(bNodeType *ntype) case TEX_NODE_OUTPUT: ntype->draw_buttons = node_texture_buts_output; break; + + case TEX_NODE_COMBINE_COLOR: + case TEX_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_texture_buts_combsep_color; + break; } } } diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 4247d5a1fbc..dfc0beb13fc 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -139,7 +139,7 @@ static void get_context_path_node_geometry(const bContext &C, Object *object = CTX_data_active_object(&C); ui::context_path_add_generic(path, RNA_Object, object); ModifierData *modifier = BKE_object_active_modifier(object); - ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_GEOMETRY_NODES); context_path_add_node_tree_and_node_groups(snode, path); } } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9076b17a926..f5048e0cc67 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -895,9 +895,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), - to_string(mesh_info.tot_verts).c_str(), - to_string(mesh_info.tot_edges).c_str(), - to_string(mesh_info.tot_faces).c_str()); + to_string(mesh_info.verts_num).c_str(), + to_string(mesh_info.edges_num).c_str(), + to_string(mesh_info.faces_num).c_str()); ss << line << line_end; break; } @@ -908,7 +908,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Point Cloud: %s points"), - to_string(pointcloud_info.tot_points).c_str()); + to_string(pointcloud_info.points_num).c_str()); ss << line << line_end; break; } @@ -918,7 +918,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Curve: %s splines"), - to_string(curve_info.tot_splines).c_str()); + to_string(curve_info.splines_num).c_str()); ss << line << line_end; break; } @@ -928,7 +928,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Instances: %s"), - to_string(instances_info.tot_instances).c_str()); + to_string(instances_info.instances_num).c_str()); ss << line << line_end; break; } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 2d7972e2291..fb2f1bf3751 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -66,7 +66,9 @@ namespace blender::ed::space_node { #define USE_ESC_COMPO -/* ***************** composite job manager ********************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Job Manager + * \{ */ enum { COM_RECALC_COMPOSITE = 1, @@ -293,6 +295,12 @@ static void compo_startjob(void *cjv, } // namespace blender::ed::space_node +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Composite Job C API + * \{ */ + void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) { using namespace blender::ed::space_node; @@ -336,9 +344,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene WM_jobs_start(CTX_wm_manager(C), wm_job); } +/** \} */ + namespace blender::ed::space_node { -/* ***************************************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Poll & Utility Functions + * \{ */ bool composite_node_active(bContext *C) { @@ -388,8 +400,14 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) } } +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Node Editor Public API Functions + * \{ */ + void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) { if (C != nullptr) { @@ -783,9 +801,13 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) // node_update_nodetree(C, ntree, 0.0f, 0.0f); } +/** \} */ + namespace blender::ed::space_node { -/* ***************** generic operator functions for nodes ***************** */ +/* -------------------------------------------------------------------- */ +/** \name Generic Operator Functions for Nodes + * \{ */ #if 0 /* UNUSED */ @@ -861,7 +883,11 @@ static void edit_node_properties_get( } #endif -/* ************************** Node generic ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Generic + * \{ */ /* is rct in visible part of node? */ static bNode *visible_node(SpaceNode &snode, const rctf &rct) @@ -874,7 +900,11 @@ static bNode *visible_node(SpaceNode &snode, const rctf &rct) return nullptr; } -/* ********************** size widget operator ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Size Widget Operator + * \{ */ struct NodeSizeWidget { float mxstart, mystart; @@ -1077,7 +1107,11 @@ void NODE_OT_resize(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING; } -/* ********************** hidden sockets ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hidden Sockets + * \{ */ bool node_has_hidden_sockets(bNode *node) { @@ -1211,7 +1245,11 @@ bool node_find_indicated_socket(SpaceNode &snode, return false; } -/* ****************** Link Dimming *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Dimming + * \{ */ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) { @@ -1237,7 +1275,11 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link) return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f; } -/* ****************** Duplicate *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Duplicate Operator + * \{ */ static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map, bNode *node) @@ -1422,8 +1464,7 @@ void node_select_all(ListBase *lb, int action) } } -/* ******************************** */ -/* XXX some code needing updating to operators. */ +/* XXX: some code needing updating to operators. */ /* goes over all scenes, reads render layers */ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1526,7 +1567,11 @@ void NODE_OT_render_changed(wmOperatorType *ot) ot->flag = 0; } -/* ****************** Hide operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hide Operator + * \{ */ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) { @@ -1722,7 +1767,11 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Mute operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Mute Operator + * \{ */ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1758,7 +1807,11 @@ void NODE_OT_mute_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete operator ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete Operator + * \{ */ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1793,7 +1846,11 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Switch View ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Switch View + * \{ */ static bool node_switch_view_poll(bContext *C) { @@ -1837,7 +1894,12 @@ void NODE_OT_switch_view_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete with reconnect ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete with Reconnect Operator + * \{ */ + static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1872,7 +1934,11 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** File Output Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node File Output Add Socket Operator + * \{ */ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) { @@ -1922,7 +1988,11 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot) ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); } -/* ****************** Multi File Output Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Remove Socket Operator + * \{ */ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1968,7 +2038,11 @@ void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Multi File Output Move Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Move Socket Node + * \{ */ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) { @@ -2040,7 +2114,11 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); } -/* ****************** Copy Node Color ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy Node Color Operator + * \{ */ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2085,7 +2163,11 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Copy to clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy to Clipboard Operator + * \{ */ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2163,7 +2245,11 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Paste from clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Paste from Clipboard + * \{ */ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { @@ -2287,7 +2373,11 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Add interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Add Interface Socket Operator + * \{ */ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) { @@ -2357,7 +2447,11 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Remove interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Remove Interface Socket Operator + * \{ */ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) { @@ -2403,7 +2497,11 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Change interface socket type operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Change Interface Socket Type Operator + * \{ */ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) { @@ -2503,7 +2601,11 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot) ot->prop = prop; } -/********************** Move interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Move Interface Socket Operator + * \{ */ static const EnumPropertyItem move_direction_items[] = { {1, "UP", 0, "Up", ""}, @@ -2577,7 +2679,11 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/* ********************** Shader Script Update ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Shader Script Update + * \{ */ static bool node_shader_script_update_poll(bContext *C) { @@ -2722,7 +2828,11 @@ void NODE_OT_shader_script_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************** Viewer border ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Viewer Border + * \{ */ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *region, @@ -2844,7 +2954,11 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Add Socket + * \{ */ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2888,7 +3002,11 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Remove Socket + * \{ */ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2933,4 +3051,7 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ + } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 10d4ad36d95..f1653e3dfd6 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -74,8 +74,18 @@ struct SpaceNode_Runtime { /** Mouse position for drawing socket-less links and adding nodes. */ float2 cursor; - /** For auto compositing. */ - bool recalc; + /** + * Indicates that the compositing tree in the space needs to be re-evaluated using the + * auto-compositing pipeline. + * Takes priority over the regular compositing. + */ + bool recalc_auto_compositing; + + /** + * Indicates that the compositing int the space tree needs to be re-evaluated using + * regular compositing pipeline. + */ + bool recalc_regular_compositing; /** Temporary data for modal linking operator. */ std::unique_ptr<bNodeLinkDrag> linkdrag; @@ -94,7 +104,7 @@ enum NodeResizeDirection { }; ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); -/* Nodes draw without dpi - the view zoom is flexible. */ +/* Nodes draw without DPI - the view zoom is flexible. */ #define HIDDEN_RAD (0.75f * U.widget_unit) #define BASIS_RAD (0.2f * U.widget_unit) #define NODE_DYS (U.widget_unit / 2) diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 1d0097068f1..147cbc5a05c 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -193,11 +193,6 @@ static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) return is_position_over_node_or_socket(*snode, mouse); } -static void node_toggle(bNode *node) -{ - nodeSetSelected(node, !(node->flag & SELECT)); -} - void node_socket_select(bNode *node, bNodeSocket &sock) { sock.flag |= SELECT; @@ -510,10 +505,10 @@ void node_select_single(bContext &C, bNode &node) WM_event_add_notifier(&C, NC_NODE | NA_SELECTED, nullptr); } -static int node_mouse_select(bContext *C, - wmOperator *op, - const int mval[2], - bool wait_to_deselect_others) +static bool node_mouse_select(bContext *C, + wmOperator *op, + const int mval[2], + struct SelectPick_Params *params) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); @@ -525,36 +520,38 @@ static int node_mouse_select(bContext *C, bNodeSocket *sock = nullptr; bNodeSocket *tsock; float cursor[2]; - int ret_value = OPERATOR_CANCELLED; - const bool extend = RNA_boolean_get(op->ptr, "extend"); /* always do socket_select when extending selection. */ - const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - /* These cases are never modal. */ - if (extend || socket_select) { - wait_to_deselect_others = false; - } + const bool socket_select = (params->sel_op == SEL_OP_XOR) || + RNA_boolean_get(op->ptr, "socket_select"); + bool changed = false; + bool found = false; + bool node_was_selected = false; /* get mouse coordinates in view2d space */ UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); /* first do socket selection, these generally overlap with nodes. */ if (socket_select) { + /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */ + const bool extend = (params->sel_op == SEL_OP_XOR); if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { + found = true; + node_was_selected = node->flag & SELECT; + /* NOTE: SOCK_IN does not take into account the extend case... * This feature is not really used anyway currently? */ node_socket_toggle(node, *sock, true); - ret_value = OPERATOR_FINISHED; + changed = true; } else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { + found = true; + node_was_selected = node->flag & SELECT; + if (sock->flag & SELECT) { if (extend) { node_socket_deselect(node, *sock, true); - } - else { - ret_value = OPERATOR_FINISHED; + changed = true; } } else { @@ -566,6 +563,7 @@ static int node_mouse_select(bContext *C, continue; } node_socket_deselect(node, *tsock, true); + changed = true; } } if (!extend) { @@ -575,69 +573,71 @@ static int node_mouse_select(bContext *C, } for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) { node_socket_deselect(tnode, *tsock, true); + changed = true; } } } node_socket_select(node, *sock); - ret_value = OPERATOR_FINISHED; + changed = true; } } } if (!sock) { + /* find the closest visible node */ node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]); + found = (node != nullptr); + node_was_selected = node && (node->flag & SELECT); - if (extend) { - if (node != nullptr) { - /* If node is selected but not active, we want to make it active, - * but not toggle (deselect) it. */ - if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { - node_toggle(node); - } - ret_value = OPERATOR_FINISHED; + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (node->flag & SELECT)) { + found = false; } - } - else if (deselect_all && node == nullptr) { - /* Rather than deselecting others, users may want to drag to box-select (drag from empty - * space) or tweak-translate an already selected item. If these cases may apply, delay - * deselection. */ - if (wait_to_deselect_others) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - /* Deselect in empty space. */ + else if (found || params->deselect_all) { + /* Deselect everything. */ for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { nodeSetSelected(tnode, false); } - ret_value = OPERATOR_FINISHED; + changed = true; } } - else if (node != nullptr) { - /* When clicking on an already selected node, we want to wait to deselect - * others and allow the user to start moving the node without that. */ - if (wait_to_deselect_others && (node->flag & SELECT)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - nodeSetSelected(node, true); - for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode != node) { - nodeSetSelected(tnode, false); - } + if (found) { + switch (params->sel_op) { + case SEL_OP_ADD: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_SUB: { + nodeSetSelected(node, false); + break; + } + case SEL_OP_XOR: { + /* Check active so clicking on an inactive node activates it. */ + bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE); + nodeSetSelected(node, !is_selected); + break; + } + case SEL_OP_SET: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } - - ret_value = OPERATOR_FINISHED; } + + changed = true; } } /* update node order */ - if (ret_value != OPERATOR_CANCELLED) { + if (changed || found) { bool active_texture_changed = false; bool viewer_node_changed = false; - if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) { + if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) { viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER; ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed); } @@ -654,23 +654,35 @@ static int node_mouse_select(bContext *C, WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } - return ret_value; + return changed || found; } static int node_select_exec(bContext *C, wmOperator *op) { - const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - /* get settings from RNA properties for operator */ int mval[2]; - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); + RNA_int_get_array(op->ptr, "location", mval); + + struct SelectPick_Params params = {}; + ED_select_pick_params_from_operator(op, ¶ms); /* perform the select */ - const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); + const bool changed = node_mouse_select(C, op, mval, ¶ms); - /* allow tweak event to work too */ - return ret_value | OPERATOR_PASS_THROUGH; + if (changed) { + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + } + /* Nothing selected, just passthrough. */ + return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED; +} + +static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_int_set_array(op->ptr, "location", event->mval); + + const int retval = node_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void NODE_OT_select(wmOperatorType *ot) @@ -684,24 +696,28 @@ void NODE_OT_select(wmOperatorType *ot) /* api callbacks */ ot->exec = node_select_exec; - ot->invoke = WM_generic_select_invoke; - ot->modal = WM_generic_select_modal; + ot->invoke = node_select_invoke; ot->poll = ED_operator_node_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - WM_operator_properties_generic_select(ot); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_int_vector(ot->srna, + "location", + 2, + NULL, + INT_MIN, + INT_MAX, + "Location", + "Mouse location", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 34f357ae5c3..348fb926d17 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -324,6 +324,41 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) return false; } +/** + * Tag the space to recalculate the compositing tree using auto-compositing pipeline. + * + * Will check the space to be using a compositing tree, and check whether auto-compositing + * is enabled. If the checks do not pass then the function has no affect. + */ +static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *area) +{ + if (!ED_node_is_compositor(snode)) { + return; + } + + if (snode->flag & SNODE_AUTO_RENDER) { + snode->runtime->recalc_auto_compositing = true; + ED_area_tag_refresh(area); + } +} + +/** + * Tag the space to recalculate the current tree. + * + * For all node trees this will do `snode_set_context()` which takes care of setting an active + * tree. This will be done in the area refresh callback. + * + * For compositor tree this will additionally start of the compositor job. + */ +static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area) +{ + if (ED_node_is_compositor(snode)) { + snode->runtime->recalc_regular_compositing = true; + } + + ED_area_tag_refresh(area); +} + static void node_area_listener(const wmSpaceTypeListenerParams *params) { ScrArea *area = params->area; @@ -346,25 +381,20 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) UI_view2d_center_set(®ion->v2d, path->view_center[0], path->view_center[1]); } - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; } case ND_FRAME: - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; case ND_COMPO_RESULT: ED_area_tag_redraw(area); break; case ND_TRANSFORM_DONE: - if (ED_node_is_compositor(snode)) { - if (snode->flag & SNODE_AUTO_RENDER) { - snode->runtime->recalc = true; - ED_area_tag_refresh(area); - } - } + node_area_tag_recalc_auto_compositing(snode, area); break; case ND_LAYER_CONTENT: - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; } break; @@ -373,46 +403,46 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_MATERIAL: if (ED_node_is_shader(snode)) { if (wmn->data == ND_SHADING) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->data == ND_SHADING_DRAW) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->data == ND_SHADING_LINKS) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } break; case NC_TEXTURE: if (ED_node_is_shader(snode) || ED_node_is_texture(snode)) { if (wmn->data == ND_NODES) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } break; case NC_WORLD: if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_WORLD) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } break; case NC_OBJECT: if (ED_node_is_shader(snode)) { if (wmn->data == ND_OB_SHADING) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } else if (ED_node_is_geometry(snode)) { /* Rather strict check: only redraw when the reference matches the current editor's ID. */ if (wmn->data == ND_MODIFIER) { if (wmn->reference == snode->id || snode->id == nullptr) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } } break; case NC_SPACE: if (wmn->data == ND_SPACE_NODE) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->data == ND_SPACE_NODE_VIEW) { ED_area_tag_redraw(area); @@ -420,7 +450,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) break; case NC_NODE: if (wmn->action == NA_EDITED) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } else if (wmn->action == NA_SELECTED) { ED_area_tag_redraw(area); @@ -429,14 +459,14 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_SCREEN: switch (wmn->data) { case ND_ANIMPLAY: - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); break; } break; case NC_MASK: if (wmn->action == NA_EDITED) { if (snode->nodetree && snode->nodetree->type == NTREE_COMPOSIT) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } break; @@ -447,7 +477,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) /* Without this check drawing on an image could become very slow when the compositor is * open. */ if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } } @@ -457,7 +487,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) if (wmn->action == NA_EDITED) { if (ED_node_is_compositor(snode)) { if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } } } @@ -465,12 +495,12 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) case NC_LINESTYLE: if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_LINESTYLE) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } break; case NC_WM: if (wmn->data == ND_UNDO) { - ED_area_tag_refresh(area); + node_area_tag_tree_recalc(snode, area); } break; case NC_GPENCIL: @@ -493,11 +523,13 @@ static void node_area_refresh(const struct bContext *C, ScrArea *area) Scene *scene = (Scene *)snode->id; if (scene->use_nodes) { /* recalc is set on 3d view changes for auto compo */ - if (snode->runtime->recalc) { - snode->runtime->recalc = false; + if (snode->runtime->recalc_auto_compositing) { + snode->runtime->recalc_auto_compositing = false; + snode->runtime->recalc_regular_compositing = false; node_render_changed_exec((struct bContext *)C, nullptr); } - else { + else if (snode->runtime->recalc_regular_compositing) { + snode->runtime->recalc_regular_compositing = false; ED_node_composite_job(C, snode->nodetree, scene); } } diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index fae0e4be2a8..59f6bd85d59 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -38,8 +38,8 @@ set(SRC tree/tree_display_data.cc tree/tree_display_libraries.cc tree/tree_display_orphaned.cc - tree/tree_display_override_library_properties.cc tree/tree_display_override_library_hierarchies.cc + tree/tree_display_override_library_properties.cc tree/tree_display_scenes.cc tree/tree_display_sequencer.cc tree/tree_display_view_layer.cc diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index d898be4eb2c..ff99416c213 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -38,6 +38,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -2504,6 +2505,11 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id) return ICON_WORKSPACE; case ID_MSK: return ICON_MOD_MASK; + case ID_NT: { + const bNodeTree *ntree = (bNodeTree *)id; + const bNodeTreeType *ntreetype = ntree->typeinfo; + return (BIFIconID)ntreetype->ui_icon; + } case ID_MC: return ICON_SEQUENCE; case ID_PC: diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index b5355efec7a..44f919ca361 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -25,10 +25,10 @@ set(INC set(SRC sequencer_add.c sequencer_buttons.c - sequencer_drag_drop.c - sequencer_draw.c sequencer_channels_draw.c sequencer_channels_edit.c + sequencer_drag_drop.c + sequencer_draw.c sequencer_edit.c sequencer_modifier.c sequencer_ops.c diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 9298eb83b46..469169cf4cc 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -136,7 +136,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) ot->srna, "overlap_shuffle_override", false, - "Override Overlap Shuffle Behaviour", + "Override Overlap Shuffle Behavior", "Use the overlap_mode tool settings to determine how to shuffle overlapping strips"); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 4afa70d9ef6..66eced27b32 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { - if (component_->attribute_domain_size(domain_) == 0) { + if (component_->attribute_domain_num(domain_) == 0) { return; } @@ -110,8 +110,8 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { - const int domain_size = component_->attribute_domain_size(domain_); - if (domain_size == 0) { + const int domain_num = component_->attribute_domain_num(domain_); + if (domain_num == 0) { return {}; } @@ -129,7 +129,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<InstanceReference> references = instances.references(); return std::make_unique<ColumnValues>( column_id.name, - VArray<InstanceReference>::ForFunc(domain_size, + VArray<InstanceReference>::ForFunc(domain_num, [reference_handles, references](int64_t index) { return references[reference_handles[index]]; })); @@ -137,13 +137,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<float4x4> transforms = instances.instance_transforms(); if (STREQ(column_id.name, "Rotation")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].to_euler(); })); } if (STREQ(column_id.name, "Scale")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].scale(); })); } @@ -210,7 +210,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( int GeometryDataSource::tot_rows() const { - return component_->attribute_domain_size(domain_); + return component_->attribute_domain_num(domain_); } /** @@ -524,17 +524,17 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, std::make_unique<GeometryComponentCacheKey>(component)); const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); for (const auto item : fields_to_show.items()) { StringRef name = item.key; const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - GArray<> evaluated_array(field.cpp_type(), domain_size); + GArray<> evaluated_array(field.cpp_type(), domain_num); bke::GeometryComponentFieldContext field_context{component, domain}; - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add_with_destination(field, evaluated_array); field_evaluator.evaluate(); return evaluated_array; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index c4b5228758c..b009b6b6e4d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const } if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) { - return component->attribute_domain_size(*domain_); + return component->attribute_domain_num(*domain_); } return 0; diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 36eafedbc80..c93ffccd477 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1603,7 +1603,7 @@ void draw_text_main(SpaceText *st, ARegion *region) return; } - /* dpi controlled line height and font size */ + /* DPI controlled line height and font size. */ st->runtime.lheight_px = (U.widget_unit * st->lheight) / 20; /* don't draw lines below this */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index dbe67bd0d66..018468dcd03 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,6 +479,45 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { bool clipx = true, clipy = true; @@ -517,31 +556,29 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) } if (resize) { - if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] && - t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) { - vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]); - } - else if (max[0] > (base_offset[0] + t->aspect[0]) && - t->center_global[0] < (base_offset[0] + t->aspect[0])) { - vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) / - (t->center_global[0] - max[0]); - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] && - t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) { - vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]); - } - else if (max[1] > (base_offset[1] + t->aspect[1]) && - t->center_global[1] < (base_offset[1] + t->aspect[1])) { - vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) / - (t->center_global[1] - max[1]); - } - else { - clipy = 0; - } + /* Assume no change is required. */ + float scalex = 1.0f; + float scaley = 1.0f; + + /* Update U against the left border. */ + constrain_scale_to_boundary( + t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], + max[0] - t->center_global[0], + &scalex); + + /* Do the same for the V co-ordinate, which is called `y`. */ + constrain_scale_to_boundary( + t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); + constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], + max[1] - t->center_global[1], + &scaley); + + clipx = (scalex != 1.0f); + clipy = (scaley != 1.0f); + vec[0] *= scalex; + vec[1] *= scaley; } else { if (min[0] < base_offset[0]) { diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index d5d79bedbf4..8281052c314 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -46,7 +46,7 @@ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const } /* use top-left corner as the transform origin for nodes */ - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ #ifdef USE_NODE_CENTER td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->totr) * +0.5f); td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->totr) * -0.5f); @@ -194,7 +194,7 @@ void flushTransNodes(TransInfo *t) loc[1] += 0.5f * BLI_rctf_size_y(&node->totr); #endif - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ loc[0] /= dpi_fac; loc[1] /= dpi_fac; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index afad4df2c88..769fd28c57b 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -15,6 +15,7 @@ #include "BLI_math.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_state.h" #include "BKE_context.h" @@ -25,6 +26,7 @@ #include "RNA_access.h" +#include "WM_api.h" #include "WM_types.h" #include "ED_gizmo_library.h" @@ -173,20 +175,20 @@ void drawSnapping(const struct bContext *C, TransInfo *t) return; } - UI_GetThemeColor3ubv(TH_TRANSFORM, col); - col[3] = 128; - - UI_GetThemeColor3ubv(TH_SELECT, selectedCol); - selectedCol[3] = 128; - - UI_GetThemeColor3ubv(TH_ACTIVE, activeCol); - activeCol[3] = 192; - if (t->spacetype == SPACE_VIEW3D) { bool draw_target = (t->tsnap.status & TARGET_INIT) && (t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR); if (draw_target || validSnap(t)) { + UI_GetThemeColor3ubv(TH_TRANSFORM, col); + col[3] = 128; + + UI_GetThemeColor3ubv(TH_SELECT, selectedCol); + selectedCol[3] = 128; + + UI_GetThemeColor3ubv(TH_ACTIVE, activeCol); + activeCol[3] = 192; + const float *loc_cur = NULL; const float *loc_prev = NULL; const float *normal = NULL; @@ -240,8 +242,26 @@ void drawSnapping(const struct bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_IMAGE) { if (validSnap(t)) { - /* This will not draw, and I'm nor sure why - campbell */ - /* TODO: see 2.7x for non-working code */ + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + float x, y; + const float snap_point[2] = { + t->tsnap.snapPoint[0] / t->aspect[0], + t->tsnap.snapPoint[1] / t->aspect[1], + }; + UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y); + float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize; + + GPU_matrix_push_projection(); + wmOrtho2_region_pixelspace(t->region); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ub(255, 255, 255); + imm_draw_circle_wire_2d(pos, x, y, radius, 8); + immUnbindProgram(); + + GPU_matrix_pop_projection(); } } else if (t->spacetype == SPACE_NODE) { @@ -990,17 +1010,19 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) { BLI_assert(t->spacetype == SPACE_IMAGE); if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) { - float co[2]; - - UI_view2d_region_to_view(&t->region->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); - uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( t->view_layer, NULL, &objects_len); - float dist_sq = FLT_MAX; - if (ED_uvedit_nearest_uv_multi( - t->scene, objects, objects_len, co, &dist_sq, t->tsnap.snapPoint)) { + float dist_sq = square_f((float)SNAP_MIN_DISTANCE); + if (ED_uvedit_nearest_uv_multi(&t->region->v2d, + t->scene, + objects, + objects_len, + t->mval, + t->tsnap.modeSelect == SNAP_NOT_SELECTED, + &dist_sq, + t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; t->tsnap.snapPoint[1] *= t->aspect[1]; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 1287804d9ee..13dac431b57 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1019,8 +1019,13 @@ bool uv_find_nearest_vert_multi(Scene *scene, return found; } -bool ED_uvedit_nearest_uv( - const Scene *scene, Object *obedit, const float co[2], float *dist_sq, float r_uv[2]) +static bool uvedit_nearest_uv(const Scene *scene, + Object *obedit, + const float co[2], + const float scale[2], + const bool ignore_selected, + float *dist_sq, + float r_uv[2]) { BMEditMesh *em = BKE_editmesh_from_object(obedit); BMIter iter; @@ -1035,8 +1040,14 @@ bool ED_uvedit_nearest_uv( BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { + if (ignore_selected && uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) { + continue; + } + const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv; - const float dist_test = len_squared_v2v2(co, uv); + float co_tmp[2]; + mul_v2_v2v2(co_tmp, scale, uv); + const float dist_test = len_squared_v2v2(co, co_tmp); if (dist_best > dist_test) { dist_best = dist_test; uv_best = uv; @@ -1052,17 +1063,27 @@ bool ED_uvedit_nearest_uv( return false; } -bool ED_uvedit_nearest_uv_multi(const Scene *scene, +bool ED_uvedit_nearest_uv_multi(const View2D *v2d, + const Scene *scene, Object **objects, const uint objects_len, - const float co[2], + const int mval[2], + const bool ignore_selected, float *dist_sq, float r_uv[2]) { bool found = false; + + float scale[2], offset[2]; + UI_view2d_scale_get(v2d, &scale[0], &scale[1]); + UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]); + + float co[2]; + sub_v2_v2v2(co, (float[2]){UNPACK2(mval)}, offset); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - if (ED_uvedit_nearest_uv(scene, obedit, co, dist_sq, r_uv)) { + if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) { found = true; } } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 34fae2ffb2a..c0ea753ed51 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -267,26 +267,35 @@ static bool uvedit_have_selection_multi(const Scene *scene, return have_select; } +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy) +{ + if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) { + *r_aspx = 1.0f; + *r_aspy = 1.0f; + return; + } + Image *ima; + ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL); + ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); +} + void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) { BMEditMesh *em = BKE_editmesh_from_object(ob); BLI_assert(em != NULL); bool sloppy = true; bool selected = false; - BMFace *efa; - Image *ima; - - efa = BM_mesh_active_face_get(em->bm, sloppy, selected); - - if (efa) { - ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL); - - ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); - } - else { + BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected); + if (!efa) { *r_aspx = 1.0f; *r_aspy = 1.0f; + return; } + + ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy); } static void construct_param_handle_face_add(ParamHandle *handle, @@ -1527,49 +1536,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius) } } -static void correct_uv_aspect(Object *ob, BMEditMesh *em) +static void shrink_loop_uv_by_aspect_ratio(BMFace *efa, + const int cd_loop_uv_offset, + const float aspect_y) { + BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */ + BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */ + BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - BMFace *efa; - float scale, aspx, aspy; + BMIter iter; + BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (aspect_y > 1.0f) { + /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */ + luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y); + } + else { + /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */ + luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y); + } + } +} +static void correct_uv_aspect(Object *ob, BMEditMesh *em) +{ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - + float aspx, aspy; ED_uvedit_get_aspect(ob, &aspx, &aspy); + const float aspect_y = aspx / aspy; + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); + } + } +} - if (aspx == aspy) { +static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em) +{ + const int materials_num = ob->totcol; + if (materials_num == 0) { + /* Without any materials, there is no aspect_y information and nothing to do. */ return; } - if (aspx > aspy) { - scale = aspy / aspx; + float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num); + /* Lazily initialize aspect ratio for materials. */ + copy_vn_fl(material_aspect_y, materials_num, -1.0f); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f; - } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; } - } - else { - scale = aspx / aspy; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int material_index = efa->mat_nr; + if (UNLIKELY(material_index < 0 || material_index >= materials_num)) { + /* The index might be for a material slot which is not currently setup. */ + continue; + } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f; - } + float aspect_y = material_aspect_y[material_index]; + if (aspect_y == -1.0f) { + /* Lazily initialize aspect ratio for materials. */ + float aspx, aspy; + ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy); + aspect_y = aspx / aspy; + material_aspect_y[material_index] = aspect_y; } + + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + continue; + } + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); } } @@ -1613,7 +1661,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) uv_map_clip_correct_properties_ex(ot, true); } -static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) +/** + * \param per_face_aspect: Calculate the aspect ratio per-face, + * otherwise use a single aspect for all UV's based on the material of the active face. + * TODO: using per-face aspect may split UV islands so more advanced UV projection methods + * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves. + * For now keep using a single aspect for all faces in this case. + */ +static void uv_map_clip_correct_multi(Object **objects, + uint objects_len, + wmOperator *op, + bool per_face_aspect) { BMFace *efa; BMLoop *l; @@ -1633,9 +1691,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper BMEditMesh *em = BKE_editmesh_from_object(ob); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* correct for image aspect ratio */ + /* Correct for image aspect ratio. */ if (correct_aspect) { - correct_uv_aspect(ob, em); + if (per_face_aspect) { + correct_uv_aspect_per_face(ob, em); + } + else { + correct_uv_aspect(ob, em); + } } if (scale_to_bounds) { @@ -1678,6 +1741,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper dy = 1.0f / dy; } + if (dx == 1.0f && dy == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; @@ -1702,7 +1770,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper static void uv_map_clip_correct(Object *ob, wmOperator *op) { - uv_map_clip_correct_multi(&ob, 1, op); + uv_map_clip_correct_multi(&ob, 1, op, true); } /** \} */ @@ -2283,7 +2351,9 @@ static int smart_project_exec(bContext *C, wmOperator *op) .use_seams = true, }); - uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */ + const bool per_face_aspect = false; + uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect); } MEM_freeN(objects_changed); @@ -2485,7 +2555,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) } if (changed_multi) { - uv_map_clip_correct_multi(objects, objects_len, op); + uv_map_clip_correct_multi(objects, objects_len, op, true); } MEM_freeN(objects); diff --git a/source/blender/freestyle/intern/geometry/SweepLine.h b/source/blender/freestyle/intern/geometry/SweepLine.h index 1165e1bf064..c170ee4d122 100644 --- a/source/blender/freestyle/intern/geometry/SweepLine.h +++ b/source/blender/freestyle/intern/geometry/SweepLine.h @@ -183,7 +183,7 @@ template<class T1, class T2> struct binary_rule { binary_rule() { } - template<class T3, class T4> binary_rule(const binary_rule<T3, T4> &brother) + template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/) { } virtual ~binary_rule() diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 8716d6c8f67..010c327d482 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -16,15 +16,19 @@ set(INC set(SRC intern/mesh_merge_by_distance.cc + intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc intern/point_merge_by_distance.cc intern/realize_instances.cc + intern/resample_curves.cc intern/uv_parametrizer.c GEO_mesh_merge_by_distance.hh + GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh GEO_point_merge_by_distance.hh GEO_realize_instances.hh + GEO_resample_curves.hh GEO_uv_parametrizer.h ) diff --git a/source/blender/geometry/GEO_mesh_primitive_cuboid.hh b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh new file mode 100644 index 00000000000..d8f16065e2b --- /dev/null +++ b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +struct Mesh; +struct float3; +namespace blender { +namespace bke { +class AttributeIDRef; +} +} // namespace blender + +namespace blender::geometry { + +Mesh *create_cuboid_mesh( + const float3 &size, int verts_x, int verts_y, int verts_z, const bke::AttributeIDRef &uv_id); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh new file mode 100644 index 00000000000..97399ccb0a5 --- /dev/null +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "FN_field.hh" + +#include "BKE_geometry_set.hh" + +struct Curves; + +namespace blender::geometry { + +/** + * Create new curves where the selected curves have been resampled with a number of uniform-length + * samples defined by the count field. Interpolate attributes to the result, with an accuracy that + * depends on the curve's resolution parameter. + * + * \note The values provided by the #count_field are clamped to 1 or greater. + */ +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field); + +/** + * Create new curves resampled to make each segment have the length specified by the + * #segment_length field input, rounded to make the length of each segment the same. + * The accuracy will depend on the curve's resolution parameter. + */ +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field); + +/** + * Evaluate each selected curve to its implicit evaluated points. + */ +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc new file mode 100644 index 00000000000..e41516d0486 --- /dev/null +++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_index_range.hh" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" + +#include "GEO_mesh_primitive_cuboid.hh" + +namespace blender::geometry { + +struct CuboidConfig { + float3 size; + int verts_x; + int verts_y; + int verts_z; + int edges_x; + int edges_y; + int edges_z; + int vertex_count; + int poly_count; + int loop_count; + + CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) + : size(size), + verts_x(verts_x), + verts_y(verts_y), + verts_z(verts_z), + edges_x(verts_x - 1), + edges_y(verts_y - 1), + edges_z(verts_z - 1) + { + BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); + this->vertex_count = this->get_vertex_count(); + this->poly_count = this->get_poly_count(); + this->loop_count = this->poly_count * 4; + } + + private: + int get_vertex_count() + { + const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); + return verts_x * verts_y * verts_z - inner_position_count; + } + + int get_poly_count() + { + return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); + } +}; + +static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) +{ + const float z_bottom = -config.size.z / 2.0f; + const float z_delta = config.size.z / config.edges_z; + + const float x_left = -config.size.x / 2.0f; + const float x_delta = config.size.x / config.edges_x; + + const float y_front = -config.size.y / 2.0f; + const float y_delta = config.size.y / config.edges_y; + + int vert_index = 0; + + for (const int z : IndexRange(config.verts_z)) { + if (ELEM(z, 0, config.edges_z)) { + /* Fill bottom and top. */ + const float z_pos = z_bottom + z_delta * z; + for (const int y : IndexRange(config.verts_y)) { + const float y_pos = y_front + y_delta * y; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + } + else { + for (const int y : IndexRange(config.verts_y)) { + if (ELEM(y, 0, config.edges_y)) { + /* Fill y-sides. */ + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + else { + /* Fill x-sides. */ + const float x_pos = x_left; + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + const float x_pos2 = x_left + x_delta * config.edges_x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); + } + } + } + } +} + +/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. + * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for + * anti-clockwise. + */ +static void define_quad(MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops, + const int poly_index, + const int loop_index, + const int vert_1, + const int vert_2, + const int vert_3, + const int vert_4) +{ + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_1 = loops[loop_index]; + loop_1.v = vert_1; + MLoop &loop_2 = loops[loop_index + 1]; + loop_2.v = vert_2; + MLoop &loop_3 = loops[loop_index + 2]; + loop_3.v = vert_3; + MLoop &loop_4 = loops[loop_index + 3]; + loop_4.v = vert_4; +} + +static void calculate_polys(const CuboidConfig &config, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) +{ + int loop_index = 0; + int poly_index = 0; + + /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ + const int xy_cross_section_vert_count = config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2); + + /* Calculate polys for Bottom faces. */ + int vert_1_start = 0; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + const int vert_1 = vert_1_start + x; + const int vert_2 = vert_1_start + config.verts_x + x; + const int vert_3 = vert_2 + 1; + const int vert_4 = vert_1 + 1; + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + vert_1_start += config.verts_x; + } + + /* Calculate polys for Front faces. */ + vert_1_start = 0; + int vert_2_start = config.verts_x * config.verts_y; + + for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_1_start = vert_2_start; + vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); + } + + /* Calculate polys for Top faces. */ + vert_1_start = config.verts_x * config.verts_y + + (config.verts_z - 2) * (config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2)); + vert_2_start = vert_1_start + config.verts_x; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_2_start += config.verts_x; + vert_1_start += config.verts_x; + } + + /* Calculate polys for Back faces. */ + vert_1_start = config.verts_x * config.edges_y; + vert_2_start = vert_1_start + xy_cross_section_vert_count; + + for (const int z : IndexRange(config.edges_z)) { + if (z == (config.edges_z - 1)) { + vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); + } + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_2_start + x, + vert_2_start + x + 1, + vert_1_start + x + 1); + loop_index += 4; + poly_index++; + } + vert_2_start += xy_cross_section_vert_count; + vert_1_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Left faces. */ + vert_1_start = 0; + vert_2_start = config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1; + int vert_2; + int vert_3; + int vert_4; + + if (z == 0 || y == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_1 += config.verts_x - 2; + vert_4 = vert_1 + 2; + } + + if (y == 0 || z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_2 += config.verts_x - 2; + vert_3 = vert_2 + 2; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Right faces. */ + vert_1_start = config.edges_x; + vert_2_start = vert_1_start + config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1 = vert_1_start; + int vert_2 = vert_2_start; + int vert_3 = vert_2_start + 2; + int vert_4 = vert_1 + config.verts_x; + + if (z == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_4 = vert_1 + 2; + } + + if (z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_3 = vert_2 + 2; + } + + if (y == (config.edges_y - 1)) { + vert_3 = vert_2 + config.verts_x; + vert_4 = vert_1 + config.verts_x; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } +} + +static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + bke::OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>(uv_id, ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); + + int loop_index = 0; + + const float x_delta = 0.25f / static_cast<float>(config.edges_x); + const float y_delta = 0.25f / static_cast<float>(config.edges_y); + const float z_delta = 0.25f / static_cast<float>(config.edges_z); + + /* Calculate bottom face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); + } + } + + /* Calculate front face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); + } + } + + /* Calculate top face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); + } + } + + /* Calculate back face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); + } + } + + /* Calculate left face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); + } + } + + /* Calculate right face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); + } + } + + uv_attribute.save(); +} + +Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z, + const bke::AttributeIDRef &uv_id) +{ + const CuboidConfig config(size, verts_x, verts_y, verts_z); + + Mesh *mesh = BKE_mesh_new_nomain( + config.vertex_count, 0, 0, config.loop_count, config.poly_count); + + calculate_vertices(config, {mesh->mvert, mesh->totvert}); + + calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); + BKE_mesh_calc_edges(mesh, false, false); + + if (uv_id) { + calculate_uvs(config, mesh, uv_id); + } + + return mesh; +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index f3f0e5b1fce..ae07e817c67 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -374,7 +374,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>( - to_type, instances_component.instances_amount()); + to_type, instances_component.instances_num()); conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span()); span = temporary_array->as_span(); gather_info.r_temporary_arrays.append(std::move(temporary_array)); @@ -548,7 +548,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component); const Curves *curves = curve_component.get_for_read(); - if (curves != nullptr && curves->geometry.curve_size > 0) { + if (curves != nullptr && curves->geometry.curve_num > 0) { const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.curves_offsets, @@ -556,8 +556,8 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size; - gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size; + gather_info.r_offsets.curves_offsets.point += curves->geometry.point_num; + gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_num; } break; } @@ -1052,7 +1052,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, VectorSet<const Curves *> &r_curves) { if (const Curves *curves = geometry_set.get_curves_for_read()) { - if (curves->geometry.curve_size != 0) { + if (curves->geometry.curve_num != 0) { r_curves.add(curves); } } @@ -1215,13 +1215,13 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const RealizeCurveTask &last_task = tasks.last(); const Curves &last_curves = *last_task.curve_info->curves; - const int points_size = last_task.start_indices.point + last_curves.geometry.point_size; - const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size; + const int points_num = last_task.start_indices.point + last_curves.geometry.point_num; + const int curves_num = last_task.start_indices.curve + last_curves.geometry.curve_num; /* Allocate new curves data-block. */ - Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size); + Curves *dst_curves_id = bke::curves_new_nomain(points_num, curves_num); bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - dst_curves.offsets_for_write().last() = points_size; + dst_curves.offsets_for_write().last() = points_num; CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); dst_component.replace(dst_curves_id); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc new file mode 100644 index 00000000000..7895225a189 --- /dev/null +++ b/source/blender/geometry/intern/resample_curves.cc @@ -0,0 +1,474 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_length_parameterize.hh" +#include "BLI_task.hh" + +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_fields.hh" + +#include "GEO_resample_curves.hh" + +namespace blender::geometry { + +static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field) +{ + static fn::CustomMF_SI_SO<int, int> max_one_fn( + "Clamp Above One", + [](int value) { return std::max(1, value); }, + fn::CustomMF_presets::AllSpanOrSingle()); + auto clamp_op = std::make_shared<fn::FieldOperation>( + fn::FieldOperation(max_one_fn, {count_field})); + + return fn::Field<int>(std::move(clamp_op)); +} + +static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field) +{ + static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( + "Length Input to Count", + [](const float curve_length, const float sample_length) { + /* Find the number of sampled segments by dividing the total length by + * the sample length. Then there is one more sampled point than segment. */ + const int count = int(curve_length / sample_length) + 1; + return std::max(1, count); + }, + fn::CustomMF_presets::AllSpanOrSingle()); + + auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation( + get_count_fn, + {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field})); + + return fn::Field<int>(std::move(get_count_op)); +} + +/** + * Return true if the attribute should be copied/interpolated to the result curves. + * Don't output attributes that correspond to curve types that have no curves in the result. + */ +static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id, + const std::array<int, CURVE_TYPES_NUM> &type_counts) +{ + if (!attribute_id.is_named()) { + return true; + } + if (ELEM(attribute_id.name(), + "handle_type_left", + "handle_type_right", + "handle_left", + "handle_right")) { + return type_counts[CURVE_TYPE_BEZIER] != 0; + } + if (ELEM(attribute_id.name(), "nurbs_weight")) { + return type_counts[CURVE_TYPE_NURBS] != 0; + } + return true; +} + +/** + * Return true if the attribute should be copied to poly curves. + */ +static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attribute_id) +{ + static const Set<StringRef> no_interpolation{{ + "handle_type_left", + "handle_type_right", + "handle_position_right", + "handle_position_left", + "nurbs_weight", + }}; + return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); +} + +/** + * Retrieve spans from source and result attributes. + */ +static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids, + const CurveComponent &src_component, + CurveComponent &dst_component, + Vector<GSpan> &src, + Vector<GMutableSpan> &dst, + Vector<bke::OutputAttribute> &dst_attributes) +{ + for (const int i : ids.index_range()) { + GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); + BLI_assert(src_attribute); + src.append(src_attribute.get_internal_span()); + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); + bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + ids[i], ATTR_DOMAIN_POINT, data_type); + dst.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } +} + +struct AttributesForInterpolation : NonCopyable, NonMovable { + Vector<GSpan> src; + Vector<GMutableSpan> dst; + + Vector<bke::OutputAttribute> dst_attributes; + + Vector<GSpan> src_no_interpolation; + Vector<GMutableSpan> dst_no_interpolation; +}; + +/** + * Gather a set of all generic attribute IDs to copy to the result curves. + */ +static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, + CurveComponent &dst_component, + AttributesForInterpolation &result) +{ + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap( + dst_component.get_for_write()->geometry); + + VectorSet<bke::AttributeIDRef> ids; + VectorSet<bke::AttributeIDRef> ids_no_interpolation; + src_component.attribute_foreach( + [&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + return true; + } + if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { + return true; + } + if (interpolate_attribute_to_poly_curve(id)) { + ids.add_new(id); + } + else { + ids_no_interpolation.add_new(id); + } + return true; + }); + + /* Position is handled differently since it has non-generic interpolation for Bezier + * curves and because the evaluated positions are cached for each evaluated point. */ + ids.remove_contained("position"); + + retrieve_attribute_spans( + ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); + + /* Attributes that aren't interpolated like Bezier handles still have to be be copied + * to the result when there are any unselected curves of the corresponding type. */ + retrieve_attribute_spans(ids_no_interpolation, + src_component, + dst_component, + result.src_no_interpolation, + result.dst_no_interpolation, + result.dst_attributes); + + dst_curves.update_customdata_pointers(); +} + +/** + * Copy the provided point attribute values between all curves in the #curve_ranges index + * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. + */ +template<typename T> +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> curve_ranges, + const Span<T> src, + const MutableSpan<T> dst) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange src_points = src_curves.points_for_curves(range); + const IndexRange dst_points = dst_curves.points_for_curves(range); + /* The arrays might be large, so a threaded copy might make sense here too. */ + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }); +} +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> unselected_ranges, + const GSpan src, + const GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); + }); +} + +static Curves *resample_to_uniform(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + /* Create the new curves without any points and evaluate the final count directly + * into the offsets array, in order to be accumulated into offsets later. */ + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(count_field, dst_offsets); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + + VArray<bool> curves_cyclic = src_curves.cyclic(); + VArray<int8_t> curve_types = src_curves.curve_types(); + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + src_curves.ensure_evaluated_lengths(); + + /* Sampling arbitrary attributes works by first interpolating them to the curve's standard + * "evaluated points" and then interpolating that result with the uniform samples. This is + * potentially wasteful when down-sampling a curve to many fewer points. There are two possible + * solutions: only sample the necessary points for interpolation, or first sample curve + * parameter/segment indices and evaluate the curve directly. */ + Array<int> sample_indices(dst_curves.points_num()); + Array<float> sample_factors(dst_curves.points_num()); + + /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on + * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a + * time or one curve at a time. */ + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + Vector<std::byte> evaluated_buffer; + + /* Gather uniform samples based on the accumulated lengths of the original curve. */ + for (const int i_curve : sliced_selection) { + const bool cyclic = curves_cyclic[i_curve]; + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::create_uniform_samples( + src_curves.evaluated_lengths_for_curve(i_curve, cyclic), + curves_cyclic[i_curve], + sample_indices.as_mutable_span().slice(dst_points), + sample_factors.as_mutable_span().slice(dst_points)); + } + + /* For every attribute, evaluate attributes from every curve in the range in the original + * curve's "evaluated points", then use linear interpolation to sample to the result. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + + if (curve_types[i_curve] == CURVE_TYPE_POLY) { + length_parameterize::linear_interpolation(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + else { + const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); + evaluated_buffer.clear(); + evaluated_buffer.resize(sizeof(T) * evaluated_size); + MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); + src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); + + length_parameterize::linear_interpolation(evaluated.as_span(), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + } + }); + } + + /* Interpolate the evaluated positions to the resampled curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst_positions.slice(dst_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + return resample_to_uniform(src_component, selection_field, get_count_input_max_one(count_field)); +} + +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field) +{ + return resample_to_uniform( + src_component, selection_field, get_count_input_from_length(segment_length_field)); +} + +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + src_curves.ensure_evaluated_offsets(); + threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); + } + }); + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* Create the correct number of uniform-length samples for every selected curve. */ + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + /* Evaluate generic point attributes directly to the result attributes. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + src_curves.interpolate_to_evaluated( + i_curve, src.slice(src_points), dst.slice(dst_points)); + } + }); + } + + /* Copy the evaluated positions to the selected curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +} // namespace blender::geometry diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 6108629183c..69fc26c99e9 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -65,14 +65,30 @@ set(SRC # Lineart code intern/lineart/lineart_chain.c + intern/lineart/lineart_cpp_bridge.cc intern/lineart/lineart_cpu.c intern/lineart/lineart_ops.c intern/lineart/lineart_util.c intern/lineart/MOD_lineart.h intern/lineart/lineart_intern.h +) + +if(WITH_TBB) +add_definitions(-DWITH_TBB) +if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) +endif() +list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} +) +list(APPEND LIB + ${TBB_LIBRARIES} ) +endif() set(LIB ) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 1058f861be3..0e7df2a136d 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -387,7 +387,6 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 5d952991cf7..99107a96cfe 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -132,7 +132,7 @@ typedef struct LineartEdge { char min_occ; /** Also for line type determination on chaining. */ - unsigned char flags; + uint16_t flags; unsigned char intersection_mask; /** @@ -171,7 +171,7 @@ typedef struct LineartEdgeChainItem { /** For restoring position to 3d space. */ float gpos[3]; float normal[3]; - unsigned char line_type; + uint16_t line_type; char occlusion; unsigned char material_mask_bits; unsigned char intersection_mask; @@ -189,6 +189,12 @@ typedef struct LineartChainRegisterEntry { char is_left; } LineartChainRegisterEntry; +typedef struct LineartAdjacentEdge { + unsigned int v1; + unsigned int v2; + unsigned int e; +} LineartAdjacentEdge; + enum eLineArtTileRecursiveLimit { /* If tile gets this small, it's already much smaller than a pixel. No need to continue * splitting. */ @@ -396,7 +402,7 @@ typedef struct LineartObjectInfo { typedef struct LineartObjectLoadTaskInfo { struct LineartRenderBuffer *rb; - struct Depsgraph *dg; + int thread_id; /* LinkNode styled list */ LineartObjectInfo *pending; /* Used to spread the load across several threads. This can not overflow. */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc new file mode 100644 index 00000000000..174399618a5 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_sort.hh" +#include "BLI_vector.hh" +#include "MOD_lineart.h" +#include "lineart_intern.h" + +static bool cmp_adjacent_items(const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) +{ + int a = p1.v1 - p2.v1; + int b = p1.v2 - p2.v2; + /* parallel_sort() requires cmp() to return true when the first element needs to appear before + * the second element in the sorted array, false otherwise (strict weak ordering), see + * https://en.cppreference.com/w/cpp/named_req/Compare. */ + return a < 0 ? true : (a == 0 ? b < 0 : false); +} + +void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length) +{ + blender::parallel_sort(ai, ai + length - 1, 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 24e11f6be3b..b09bb15ce81 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -8,6 +8,7 @@ #include "MOD_gpencil_lineart.h" #include "MOD_lineart.h" +#include "BLI_edgehash.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -28,6 +29,8 @@ #include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_runtime.h" #include "BKE_object.h" #include "BKE_pointcache.h" #include "BKE_scene.h" @@ -1426,17 +1429,45 @@ static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) } } -/** - * Transform a single vert to it's viewing position. - */ -static void lineart_vert_transform( - BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4]) +typedef struct LineartEdgeNeighbor { + int e; + uint16_t flags; + int v1, v2; +} LineartEdgeNeighbor; + +typedef struct VertData { + MVert *mvert; + LineartVert *v_arr; + double (*model_view)[4]; + double (*model_view_proj)[4]; +} VertData; + +static void lineart_mvert_transform_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { + VertData *vert_task_data = (VertData *)userdata; + MVert *m_v = &vert_task_data->mvert[i]; double co[4]; - LineartVert *vt = &RvBuf[index]; - copy_v3db_v3fl(co, v->co); - mul_v3_m4v3_db(vt->gloc, mv_mat, co); - mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co); + LineartVert *v = &vert_task_data->v_arr[i]; + copy_v3db_v3fl(co, m_v->co); + mul_v3_m4v3_db(v->gloc, vert_task_data->model_view, co); + mul_v4_m4v3_db(v->fbcoord, vert_task_data->model_view_proj, co); + v->index = i; +} + +#define LRT_EDGE_FLAG_TYPE_MAX_BITS 6 + +static int lineart_edge_type_duplication_count(char eflag) +{ + int count = 0; + /* See eLineartEdgeFlag for details. */ + for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) { + if (eflag & (1 << i)) { + count++; + } + } + return count; } /** @@ -1452,88 +1483,123 @@ static LineartTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb, return (LineartTriangle *)b; } -static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, - BMEdge *e, - LineartTriangle *rt_array, - LineartVert *rv_array, - float crease_threshold, - bool use_auto_smooth, - bool use_freestyle_edge, - bool use_freestyle_face, - BMesh *bm_if_freestyle) +typedef struct EdgeFeatData { + LineartRenderBuffer *rb; + Mesh *me; + const MLoopTri *mlooptri; + LineartTriangle *tri_array; + LineartVert *v_array; + float crease_threshold; + bool use_auto_smooth; + bool use_freestyle_face; + int freestyle_face_index; + bool use_freestyle_edge; + int freestyle_edge_index; + LineartEdgeNeighbor *edge_nabr; +} EdgeFeatData; + +typedef struct EdgeFeatReduceData { + int feat_edges; +} EdgeFeatReduceData; + +static void feat_data_sum_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + EdgeFeatReduceData *feat_chunk_join = (EdgeFeatReduceData *)chunk_join; + EdgeFeatReduceData *feat_chunk = (EdgeFeatReduceData *)chunk; + feat_chunk_join->feat_edges += feat_chunk->feat_edges; +} + +static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict tls) { - BMLoop *ll, *lr = NULL; + EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata; + EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk; + Mesh *me = e_feat_data->me; + LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr; + const MLoopTri *mlooptri = e_feat_data->mlooptri; - ll = e->l; - if (ll) { - lr = e->l->radial_next; - } + uint16_t edge_flag_result = 0; - if (!ll && !lr) { - return LRT_EDGE_FLAG_LOOSE; + /* Because the edge neighbor array contains loop edge pairs, we only need to process the first + * edge in the pair. Otherwise we would add the same edge that the loops represent twice. */ + if (i < edge_nabr[i].e) { + return; } - FreestyleEdge *fel, *fer; bool face_mark_filtered = false; - uint16_t edge_flag_result = 0; + bool enable_face_mark = (e_feat_data->use_freestyle_face && e_feat_data->rb->filter_face_mark); bool only_contour = false; - - if (use_freestyle_face && rb->filter_face_mark) { - fel = CustomData_bmesh_get(&bm_if_freestyle->pdata, ll->f->head.data, CD_FREESTYLE_FACE); - if (ll != lr && lr) { - fer = CustomData_bmesh_get(&bm_if_freestyle->pdata, lr->f->head.data, CD_FREESTYLE_FACE); + if (enable_face_mark) { + FreestyleFace *ff1, *ff2; + int index = e_feat_data->freestyle_face_index; + if (index > -1) { + ff1 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[i / 3].poly]; + } + if (edge_nabr[i].e > -1) { + ff2 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[edge_nabr[i].e / 3].poly]; } else { - /* Handles mesh boundary case */ - fer = fel; + /* Handle mesh boundary cases: We want mesh boundaries to respect + * `filter_face_mark_boundaries` option the same way as face mark boundaries, and the code + * path is simper when it's assuming both ff1 and ff2 not NULL. */ + ff2 = ff1; } - if (rb->filter_face_mark_boundaries ^ rb->filter_face_mark_invert) { - if ((fel->flag & FREESTYLE_FACE_MARK) || (fer->flag & FREESTYLE_FACE_MARK)) { + if (e_feat_data->rb->filter_face_mark_boundaries ^ e_feat_data->rb->filter_face_mark_invert) { + if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) { face_mark_filtered = true; } } else { - if ((fel->flag & FREESTYLE_FACE_MARK) && (fer->flag & FREESTYLE_FACE_MARK) && (fer != fel)) { + if ((ff1->flag & FREESTYLE_FACE_MARK) && (ff2->flag & FREESTYLE_FACE_MARK) && (ff2 != ff1)) { face_mark_filtered = true; } } - if (rb->filter_face_mark_invert) { + if (e_feat_data->rb->filter_face_mark_invert) { face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { - if (rb->filter_face_mark_keep_contour) { + edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT; + if (e_feat_data->rb->filter_face_mark_keep_contour) { only_contour = true; } - else { - return 0; - } } } + if (enable_face_mark && !face_mark_filtered && !only_contour) { + return; + } + /* Mesh boundary */ - if (!lr || ll == lr) { - return (edge_flag_result | LRT_EDGE_FLAG_CONTOUR); + if (edge_nabr[i].e == -1) { + edge_nabr[i].flags = LRT_EDGE_FLAG_CONTOUR; + reduce_data->feat_edges += 1; + return; } LineartTriangle *tri1, *tri2; - LineartVert *l; + LineartVert *vert; + LineartRenderBuffer *rb = e_feat_data->rb; + + int f1 = i / 3, f2 = edge_nabr[i].e / 3; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); - tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); + tri1 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f1); + tri2 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f2); - l = &rv_array[BM_elem_index_get(e->v1)]; + vert = &e_feat_data->v_array[edge_nabr[i].v1]; - double vv[3]; - double *view_vector = vv; + double view_vector_persp[3]; + double *view_vector = view_vector_persp; double dot_1 = 0, dot_2 = 0; double result; bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING); if (rb->use_contour || rb->use_back_face_culling || material_back_face) { - if (rb->cam_is_persp) { - sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc); + sub_v3_v3v3_db(view_vector, rb->camera_pos, vert->gloc); } else { view_vector = rb->view_vector; @@ -1542,11 +1608,10 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, dot_1 = dot_v3v3_db(view_vector, tri1->gn); dot_2 = dot_v3v3_db(view_vector, tri2->gn); - if (rb->use_contour && (result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { + if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - /* Because the ray points towards the camera, so back-face is when dot value being negative. */ if (rb->use_back_face_culling) { if (dot_1 < 0) { tri1->flags |= LRT_CULL_DISCARD; @@ -1564,56 +1629,129 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, } } } - else { - view_vector = rb->view_vector; - } - if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) { - edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; - } + if (!only_contour) { - /* For when face mark filtering decided that we discard the face but keep_contour option is on. - * so we still have correct full contour around the object. */ - if (only_contour) { - return edge_flag_result; - } + if (rb->use_crease) { + bool do_crease = true; + if (!rb->force_crease && !e_feat_data->use_auto_smooth && + (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) && + (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) { + do_crease = false; + } + if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) { + edge_flag_result |= LRT_EDGE_FLAG_CREASE; + } + } + + int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr; + int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr; - /* Do not show lines other than contour on back face (because contour has one adjacent face that - * isn't a back face). - * TODO(Yiming): Do we need separate option for this? */ - if (rb->use_back_face_culling || - ((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) { - if (dot_1 < 0 && dot_2 < 0) { - return edge_flag_result; + if (rb->use_material && mat1 != mat2) { + edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; } } + else { /* only_contour */ + if (!edge_flag_result) { /* Other edge types inhibited */ + return; + } + } + + int real_edges[3]; + BKE_mesh_looptri_get_real_edges(me, &mlooptri[i / 3], real_edges); + + if (real_edges[i % 3] >= 0) { + MEdge *medge = &me->medge[real_edges[i % 3]]; - if (rb->use_crease) { - if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { + if (rb->use_crease && rb->sharp_as_crease && (medge->flag & ME_SHARP)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } - else { - bool do_crease = true; - if (!rb->force_crease && !use_auto_smooth && - (BM_elem_flag_test(ll->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(lr->f, BM_ELEM_SMOOTH))) { - do_crease = false; - } - if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { - edge_flag_result |= LRT_EDGE_FLAG_CREASE; + + if (rb->use_edge_marks && e_feat_data->use_freestyle_edge) { + FreestyleEdge *fe; + int index = e_feat_data->freestyle_edge_index; + fe = &((FreestyleEdge *)me->edata.layers[index].data)[real_edges[i % 3]]; + if (fe->flag & FREESTYLE_EDGE_MARK) { + edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; } } } - if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) { - edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + + edge_nabr[i].flags = edge_flag_result; + + if (edge_flag_result) { + /* Only allocate for feature edge (instead of all edges) to save memory. + * If allow duplicated edges, one edge gets added multiple times if it has multiple types. + */ + reduce_data->feat_edges += e_feat_data->rb->allow_duplicated_types ? + lineart_edge_type_duplication_count(edge_flag_result) : + 1; } - if (use_freestyle_edge && rb->use_edge_marks) { - FreestyleEdge *fe; - fe = CustomData_bmesh_get(&bm_if_freestyle->edata, e->head.data, CD_FREESTYLE_EDGE); - if (fe->flag & FREESTYLE_EDGE_MARK) { - edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; - } +} + +typedef struct LooseEdgeData { + int loose_count; + int loose_max; + MEdge **loose_array; + Mesh *me; +} LooseEdgeData; + +static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count) +{ + MEdge **new_arr = MEM_callocN(sizeof(MEdge *) * count, "loose edge array"); + if (loose_data->loose_array) { + memcpy(new_arr, loose_data->loose_array, sizeof(MEdge *) * loose_data->loose_max); + MEM_freeN(loose_data->loose_array); + } + loose_data->loose_max = count; + loose_data->loose_array = new_arr; +} + +static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData *to_be_joined) +{ + if (!to_be_joined->loose_array) { + return; } - return edge_flag_result; + int new_count = loose_data->loose_count + to_be_joined->loose_count; + if (new_count >= loose_data->loose_max) { + lineart_loose_data_reallocate(loose_data, new_count); + } + memcpy(&loose_data->loose_array[loose_data->loose_count], + to_be_joined->loose_array, + sizeof(MEdge *) * to_be_joined->loose_count); + loose_data->loose_count += to_be_joined->loose_count; + MEM_freeN(to_be_joined->loose_array); +} + +static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e) +{ + if (loose_data->loose_count >= loose_data->loose_max) { + int min_amount = MAX2(100, loose_data->loose_count * 2); + lineart_loose_data_reallocate(loose_data, min_amount); + } + loose_data->loose_array[loose_data->loose_count] = e; + loose_data->loose_count++; +} + +static void lineart_identify_loose_edges(void *__restrict UNUSED(userdata), + const int i, + const TaskParallelTLS *__restrict tls) +{ + LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk; + Mesh *me = loose_data->me; + + if (me->medge[i].flag & ME_LOOSEEDGE) { + lineart_add_loose_edge(loose_data, &me->medge[i]); + } +} + +static void loose_data_sum_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + LooseEdgeData *final = (LooseEdgeData *)chunk_join; + LooseEdgeData *loose_chunk = (LooseEdgeData *)chunk; + lineart_join_loose_edge_arr(final, loose_chunk); } static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) @@ -1702,298 +1840,421 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri, } } -static int lineart_edge_type_duplication_count(char eflag) +typedef struct TriData { + LineartObjectInfo *ob_info; + const MLoopTri *mlooptri; + LineartVert *vert_arr; + LineartTriangle *tri_arr; + int lineart_triangle_size; + LineartTriangleAdjacent *tri_adj; +} TriData; + +static void lineart_load_tri_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { - int count = 0; - /* See eLineartEdgeFlag for details. */ - for (int i = 0; i < 6; i++) { - if (eflag & (1 << i)) { - count++; - } - } - return count; + TriData *tri_task_data = (TriData *)userdata; + Mesh *me = tri_task_data->ob_info->original_me; + LineartObjectInfo *ob_info = tri_task_data->ob_info; + const MLoopTri *mlooptri = &tri_task_data->mlooptri[i]; + LineartVert *vert_arr = tri_task_data->vert_arr; + LineartTriangle *tri = tri_task_data->tri_arr; + + tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i); + + int v1 = me->mloop[mlooptri->tri[0]].v; + int v2 = me->mloop[mlooptri->tri[1]].v; + int v3 = me->mloop[mlooptri->tri[2]].v; + + tri->v[0] = &vert_arr[v1]; + tri->v[1] = &vert_arr[v2]; + tri->v[2] = &vert_arr[v3]; + + /* Material mask bits and occlusion effectiveness assignment. */ + Material *mat = BKE_object_material_get(ob_info->original_ob, + me->mpoly[mlooptri->poly].mat_nr + 1); + tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? + mat->lineart.material_mask_bits : + 0); + tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); + tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? + LRT_TRIANGLE_MAT_BACK_FACE_CULLING : + 0; + + tri->intersection_mask = ob_info->override_intersection_mask; + + double gn[3]; + float no[3]; + normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co); + copy_v3db_v3fl(gn, no); + mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn); + normalize_v3_db(tri->gn); + + if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) { + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; + } + else if (ob_info->usage == OBJECT_LRT_NO_INTERSECTION || + ob_info->usage == OBJECT_LRT_OCCLUSION_ONLY) { + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; + } + + /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ + tri->intersecting_verts = (void *)&tri_task_data->tri_adj[i]; } -static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) +typedef struct EdgeNeighborData { + LineartEdgeNeighbor *edge_nabr; + LineartAdjacentEdge *adj_e; + MLoopTri *mlooptri; + MLoop *mloop; +} EdgeNeighborData; + +static void lineart_edge_neighbor_init_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { - BMesh *bm; - BMVert *v; - BMFace *f; - BMEdge *e; - BMLoop *loop; - LineartEdge *la_e; - LineartEdgeSegment *la_s; - LineartTriangle *tri; - LineartTriangleAdjacent *orta; - double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view, - (*normal)[4] = obi->normal; - LineartElementLinkNode *eln; - LineartVert *orv; - LineartEdge *o_la_e; - LineartEdgeSegment *o_la_s; - LineartTriangle *ort; - Object *orig_ob; - bool can_find_freestyle_edge = false; - bool can_find_freestyle_face = false; - int i; - float use_crease = 0; + EdgeNeighborData *en_data = (EdgeNeighborData *)userdata; + LineartAdjacentEdge *adj_e = &en_data->adj_e[i]; + MLoopTri *looptri = &en_data->mlooptri[i / 3]; + LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i]; + MLoop *mloop = en_data->mloop; + + adj_e->e = i; + adj_e->v1 = mloop[looptri->tri[i % 3]].v; + adj_e->v2 = mloop[looptri->tri[(i + 1) % 3]].v; + if (adj_e->v1 > adj_e->v2) { + SWAP(unsigned int, adj_e->v1, adj_e->v2); + } + edge_nabr->e = -1; + + edge_nabr->v1 = adj_e->v1; + edge_nabr->v2 = adj_e->v2; + edge_nabr->flags = 0; +} - int usage = obi->usage; +static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edges) +{ + /* Because the mesh is triangulated, so `me->totedge` should be reliable? */ + LineartAdjacentEdge *adj_e = MEM_mallocN(sizeof(LineartAdjacentEdge) * total_edges, + "LineartAdjacentEdge arr"); + LineartEdgeNeighbor *edge_nabr = MEM_mallocN(sizeof(LineartEdgeNeighbor) * total_edges, + "LineartEdgeNeighbor arr"); - if (obi->original_me->edit_mesh) { - /* Do not use edit_mesh directly because we will modify it, so create a copy. */ - bm = BM_mesh_copy(obi->original_me->edit_mesh->bm); - } - else { - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me))); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - obi->original_me, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); - } + MLoopTri *mlooptri = me->runtime.looptris.array; - if (obi->free_use_mesh) { - BKE_id_free(NULL, obi->original_me); - } + TaskParallelSettings en_settings; + BLI_parallel_range_settings_defaults(&en_settings); + /* Set the minimum amount of edges a thread has to process. */ + en_settings.min_iter_per_thread = 50000; + + EdgeNeighborData en_data; + en_data.adj_e = adj_e; + en_data.edge_nabr = edge_nabr; + en_data.mlooptri = mlooptri; + en_data.mloop = me->mloop; + + BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings); + + lineart_sort_adjacent_items(adj_e, total_edges); - if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm); - BMOperator findop, weldop; + for (int i = 0; i < total_edges - 1; i++) { + if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) { + edge_nabr[adj_e[i].e].e = adj_e[i + 1].e; + edge_nabr[adj_e[i + 1].e].e = adj_e[i].e; + } + } - /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ - BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); + MEM_freeN(adj_e); - BMO_op_exec(bm, &findop); + return edge_nabr; +} - /* Weld the vertices. */ - BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); - BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); - BMO_op_exec(bm, &weldop); +static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRenderBuffer *re_buf) +{ + LineartElementLinkNode *elem_link_node; + LineartVert *la_v_arr; + LineartEdge *la_edge_arr; + LineartEdgeSegment *la_seg_arr; + LineartTriangle *la_tri_arr; - BMO_op_finish(bm, &findop); - BMO_op_finish(bm, &weldop); + Mesh *me = ob_info->original_me; - MEM_freeN(em); + if (!me->totedge) { + return; } - BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); - BM_mesh_normals_update(bm); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + /* Triangulate. */ + const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me); + const int tot_tri = BKE_mesh_runtime_looptri_len(me); - if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { - can_find_freestyle_edge = 1; + /* Check if we should look for custom data tags like Freestyle edges or faces. */ + bool can_find_freestyle_edge = false; + int layer_index = CustomData_get_active_layer_index(&me->edata, CD_FREESTYLE_EDGE); + if (layer_index != -1) { + can_find_freestyle_edge = true; } - if (CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) { + + bool can_find_freestyle_face = false; + layer_index = CustomData_get_active_layer_index(&me->pdata, CD_FREESTYLE_FACE); + if (layer_index != -1) { can_find_freestyle_face = true; } /* If we allow duplicated edges, one edge should get added multiple times if is has been * classified as more than one edge type. This is so we can create multiple different line type * chains containing the same edge. */ - orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); - ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size); + la_v_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartVert) * me->totvert); + la_tri_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + tot_tri * re_buf->triangle_size); - orig_ob = obi->original_ob; + Object *orig_ob = ob_info->original_ob; - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->vertex_buffer_pointers, + &re_buf->render_data_pool, + la_v_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); - eln->element_count = bm->totvert; - eln->object_ref = orig_ob; - obi->v_eln = eln; + elem_link_node->element_count = me->totvert; + elem_link_node->object_ref = orig_ob; + ob_info->v_eln = elem_link_node; bool use_auto_smooth = false; + float crease_angle = 0; if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { - use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); + crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold); } - else if (obi->original_me->flag & ME_AUTOSMOOTH) { - use_crease = cosf(obi->original_me->smoothresh); + else if (ob_info->original_me->flag & ME_AUTOSMOOTH) { + crease_angle = cosf(ob_info->original_me->smoothresh); use_auto_smooth = true; } else { - use_crease = rb->crease_threshold; + crease_angle = re_buf->crease_threshold; } /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates * erroneous detection on creases. Future configuration should allow options. */ if (orig_ob->type == OB_FONT) { - eln->flags |= LRT_ELEMENT_BORDER_ONLY; + elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY; } - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->triangle_buffer_pointers, + &re_buf->render_data_pool, + la_tri_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); - eln->element_count = bm->totface; - eln->object_ref = orig_ob; - eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); + int usage = ob_info->usage; + + elem_link_node->element_count = tot_tri; + elem_link_node->object_ref = orig_ob; + elem_link_node->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); /* Note this memory is not from pool, will be deleted after culling. */ - orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); + LineartTriangleAdjacent *tri_adj = MEM_callocN(sizeof(LineartTriangleAdjacent) * tot_tri, + "LineartTriangleAdjacent"); /* Link is minimal so we use pool anyway. */ - BLI_spin_lock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); lineart_list_append_pointer_pool_thread( - &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); - BLI_spin_unlock(&rb->lock_task); - - for (i = 0; i < bm->totvert; i++) { - v = BM_vert_at_index(bm, i); - lineart_vert_transform(v, i, orv, model_view, model_view_proj); - orv[i].index = i; - } + &re_buf->triangle_adjacent_pointers, &re_buf->render_data_pool, tri_adj); + BLI_spin_unlock(&re_buf->lock_task); + + /* Convert all vertices to lineart verts. */ + TaskParallelSettings vert_settings; + BLI_parallel_range_settings_defaults(&vert_settings); + /* Set the minimum amount of verts a thread has to process. */ + vert_settings.min_iter_per_thread = 4000; + + VertData vert_data; + vert_data.mvert = me->mvert; + vert_data.v_arr = la_v_arr; + vert_data.model_view = ob_info->model_view; + vert_data.model_view_proj = ob_info->model_view_proj; + + BLI_task_parallel_range( + 0, me->totvert, &vert_data, lineart_mvert_transform_task, &vert_settings); /* Register a global index increment. See #lineart_triangle_share_edge() and * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually * overflow, in such large scene it's virtually impossible for two vertex of the same numeric * index to come close together. */ - obi->global_i_offset = bm->totvert; - - tri = ort; - for (i = 0; i < bm->totface; i++) { - f = BM_face_at_index(bm, i); - - loop = f->l_first; - tri->v[0] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[1] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[2] = &orv[BM_elem_index_get(loop->v)]; - - /* Material mask bits and occlusion effectiveness assignment. */ - Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1); - tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? - mat->lineart.material_mask_bits : - 0); - tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); - tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? - LRT_TRIANGLE_MAT_BACK_FACE_CULLING : - 0; - - tri->intersection_mask = obi->override_intersection_mask; - - double gn[3]; - copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(tri->gn, normal, gn); - normalize_v3_db(tri->gn); - - if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; - } - else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; - } - - /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - tri->intersecting_verts = (void *)&orta[i]; - - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); - } - - /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ - - int allocate_la_e = 0; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); - - /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - uint16_t eflag = lineart_identify_feature_line(rb, - e, - ort, - orv, - use_crease, - use_auto_smooth, - can_find_freestyle_edge, - can_find_freestyle_face, - bm); - if (eflag) { - /* Only allocate for feature lines (instead of all lines) to save memory. - * If allow duplicated edges, one edge gets added multiple times if it has multiple types. - */ - allocate_la_e += rb->allow_duplicated_types ? lineart_edge_type_duplication_count(eflag) : 1; + ob_info->global_i_offset = me->totvert; + + /* Convert all mesh triangles into lineart triangles. + * Also create an edge map to get connectivity between edges and triangles. */ + TaskParallelSettings tri_settings; + BLI_parallel_range_settings_defaults(&tri_settings); + /* Set the minimum amount of triangles a thread has to process. */ + tri_settings.min_iter_per_thread = 4000; + + TriData tri_data; + tri_data.ob_info = ob_info; + tri_data.mlooptri = mlooptri; + tri_data.vert_arr = la_v_arr; + tri_data.tri_arr = la_tri_arr; + tri_data.lineart_triangle_size = re_buf->triangle_size; + tri_data.tri_adj = tri_adj; + + unsigned int total_edges = tot_tri * 3; + + BLI_task_parallel_range(0, tot_tri, &tri_data, lineart_load_tri_task, &tri_settings); + + /* Check for contour lines in the mesh. + * IE check if the triangle edges lies in area where the triangles go from front facing to back + * facing. + */ + EdgeFeatReduceData edge_reduce = {0}; + TaskParallelSettings edge_feat_settings; + BLI_parallel_range_settings_defaults(&edge_feat_settings); + /* Set the minimum amount of edges a thread has to process. */ + edge_feat_settings.min_iter_per_thread = 4000; + edge_feat_settings.userdata_chunk = &edge_reduce; + edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData); + edge_feat_settings.func_reduce = feat_data_sum_reduce; + + EdgeFeatData edge_feat_data = {0}; + edge_feat_data.rb = re_buf; + edge_feat_data.me = me; + edge_feat_data.mlooptri = mlooptri; + edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges); + edge_feat_data.tri_array = la_tri_arr; + edge_feat_data.v_array = la_v_arr; + edge_feat_data.crease_threshold = crease_angle; + edge_feat_data.use_auto_smooth = use_auto_smooth; + edge_feat_data.use_freestyle_face = can_find_freestyle_face; + edge_feat_data.use_freestyle_edge = can_find_freestyle_edge; + if (edge_feat_data.use_freestyle_face) { + edge_feat_data.freestyle_face_index = CustomData_get_layer_index(&me->pdata, + CD_FREESTYLE_FACE); + } + if (edge_feat_data.use_freestyle_edge) { + edge_feat_data.freestyle_edge_index = CustomData_get_layer_index(&me->edata, + CD_FREESTYLE_EDGE); + } + + BLI_task_parallel_range(0, + total_edges, + &edge_feat_data, + lineart_identify_mlooptri_feature_edges, + &edge_feat_settings); + + LooseEdgeData loose_data = {0}; + if (re_buf->use_loose) { + /* Only identifying floating edges at this point because other edges has been taken care of + * inside #lineart_identify_mlooptri_feature_edges function. */ + TaskParallelSettings edge_loose_settings; + BLI_parallel_range_settings_defaults(&edge_loose_settings); + edge_loose_settings.min_iter_per_thread = 4000; + edge_loose_settings.func_reduce = loose_data_sum_reduce; + edge_loose_settings.userdata_chunk = &loose_data; + edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData); + loose_data.me = me; + BLI_task_parallel_range( + 0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings); + } + + int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count; + + la_edge_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartEdge) * allocate_la_e); + la_seg_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartEdgeSegment) * allocate_la_e); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->line_buffer_pointers, + &re_buf->render_data_pool, + la_edge_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); + elem_link_node->element_count = allocate_la_e; + elem_link_node->object_ref = orig_ob; + + // Start of the edge/seg arr + LineartEdge *la_edge; + LineartEdgeSegment *la_seg; + la_edge = la_edge_arr; + la_seg = la_seg_arr; + + for (int i = 0; i < total_edges; i++) { + LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i]; + + if (i < edge_nabr->e) { + continue; } - /* Here we just use bm's flag for when loading actual lines, then we don't need to call - * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always - * set the flag, so hflag stays 0 for lines that are not feature lines. */ - e->head.hflag = eflag; - } - - o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool, - sizeof(LineartEdgeSegment) * allocate_la_e); - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); - eln->element_count = allocate_la_e; - eln->object_ref = orig_ob; - - la_e = o_la_e; - la_s = o_la_s; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); /* Not a feature line, so we skip. */ - if (!e->head.hflag) { + if (edge_nabr->flags == 0) { continue; } bool edge_added = false; /* See eLineartEdgeFlag for details. */ - for (int flag_bit = 0; flag_bit < 6; flag_bit++) { + for (int flag_bit = 0; flag_bit < LRT_EDGE_FLAG_TYPE_MAX_BITS; flag_bit++) { char use_type = 1 << flag_bit; - if (!(use_type & e->head.hflag)) { + if (!(use_type & edge_nabr->flags)) { continue; } - la_e->v1 = &orv[BM_elem_index_get(e->v1)]; - la_e->v2 = &orv[BM_elem_index_get(e->v2)]; - la_e->v1_obindex = la_e->v1->index; - la_e->v2_obindex = la_e->v2->index; - if (e->l) { - int findex = BM_elem_index_get(e->l->f); - la_e->t1 = lineart_triangle_from_index(rb, ort, findex); + la_edge->v1 = &la_v_arr[edge_nabr->v1]; + la_edge->v2 = &la_v_arr[edge_nabr->v2]; + la_edge->v1_obindex = la_edge->v1->index; + la_edge->v2_obindex = la_edge->v2->index; + int findex = i / 3; + la_edge->t1 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + if (!edge_added) { + lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge); + } + if (edge_nabr->e != -1) { + findex = edge_nabr->e / 3; + la_edge->t2 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); if (!edge_added) { - lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); - } - if (e->l->radial_next && e->l->radial_next != e->l) { - findex = BM_elem_index_get(e->l->radial_next->f); - la_e->t2 = lineart_triangle_from_index(rb, ort, findex); - if (!edge_added) { - lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); - } + lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge); } } - la_e->flags = use_type; - la_e->object_ref = orig_ob; - BLI_addtail(&la_e->segments, la_s); - if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { - lineart_add_edge_to_list_thread(obi, la_e); + la_edge->flags = use_type; + la_edge->object_ref = orig_ob; + BLI_addtail(&la_edge->segments, la_seg); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_list_thread(ob_info, la_edge); } edge_added = true; - la_e++; - la_s++; + la_edge++; + la_seg++; - if (!rb->allow_duplicated_types) { + if (!re_buf->allow_duplicated_types) { break; } } } - /* always free bm as it's a copy from before threading */ - BM_mesh_free(bm); + if (loose_data.loose_array) { + for (int i = 0; i < loose_data.loose_count; i++) { + la_edge->v1 = &la_v_arr[loose_data.loose_array[i]->v1]; + la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2]; + la_edge->v1_obindex = la_edge->v1->index; + la_edge->v2_obindex = la_edge->v2->index; + la_edge->flags = LRT_EDGE_FLAG_LOOSE; + la_edge->object_ref = orig_ob; + BLI_addtail(&la_edge->segments, la_seg); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_list_thread(ob_info, la_edge); + } + la_edge++; + la_seg++; + } + MEM_freeN(loose_data.loose_array); + } + + MEM_freeN(edge_feat_data.edge_nabr); + + if (ob_info->free_use_mesh) { + BKE_id_free(NULL, me); + } } static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), @@ -2001,6 +2262,9 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), { for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { lineart_geometry_object_load(obi, olti->rb); + if (G.debug_value == 4000) { + printf("thread id: %d processed: %d\n", olti->thread_id, obi->original_me->totpoly); + } } } @@ -2085,6 +2349,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ use_olti = &olti_list[i]; } } + use_olti->total_faces += this_face_count; obi->next = use_olti->pending; use_olti->pending = obi; @@ -2093,18 +2358,21 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], double shift_x, double shift_y, - Object *use_ob) + Mesh *use_mesh) { - const BoundBox *bb = BKE_object_boundbox_get(use_ob); - if (!bb) { - /* For lights and empty stuff there will be no bbox. */ + if (!use_mesh) { return false; } + float mesh_min[3], mesh_max[3]; + INIT_MINMAX(mesh_min, mesh_max); + BKE_mesh_minmax(use_mesh, mesh_min, mesh_max); + BoundBox bb = {0}; + BKE_boundbox_init_from_minmax(&bb, mesh_min, mesh_max); double co[8][4]; double tmp[3]; for (int i = 0; i < 8; i++) { - copy_v3db_v3fl(co[i], bb->vec[i]); + copy_v3db_v3fl(co[i], bb.vec[i]); copy_v3_v3_db(tmp, co[i]); mul_v4_m4v3_db(co[i], model_view_proj, tmp); co[i][0] -= shift_x * 2 * co[i][3]; @@ -2216,20 +2484,13 @@ static void lineart_main_load_geometries( continue; } - if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) { - if (G.debug_value == 4000) { - bound_box_discard_count++; - } - continue; - } - if (use_ob->type == OB_MESH) { use_mesh = BKE_object_get_evaluated_mesh(use_ob); } else { /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh - * object in the list. To avoid adding duplicate geometry, ignore evaluated curve objects in - * those cases. */ + * object in the list. To avoid adding duplicate geometry, ignore evaluated curve objects + * in those cases. */ if (allow_duplicates && BKE_object_get_evaluated_mesh(ob) != NULL) { continue; } @@ -2241,6 +2502,17 @@ static void lineart_main_load_geometries( continue; } + if (!lineart_geometry_check_visible( + obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) { + if (ob->type != OB_MESH) { + BKE_id_free(NULL, use_mesh); + } + if (G.debug_value == 4000) { + bound_box_discard_count++; + } + continue; + } + if (ob->type != OB_MESH) { obi->free_use_mesh = true; } @@ -2259,9 +2531,12 @@ static void lineart_main_load_geometries( TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); + if (G.debug_value == 4000) { + printf("thread count: %d\n", thread_count); + } for (int i = 0; i < thread_count; i++) { olti[i].rb = rb; - olti[i].dg = depsgraph; + olti[i].thread_id = i; BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); } BLI_task_pool_work_and_wait(tp); @@ -3003,7 +3278,7 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; - /* If this is not the smallest subdivision bounding area. */ + /* If this is not the smallest subdiv bounding area. */ if (ba->child) { lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]); lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]); @@ -3187,7 +3462,6 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; - rb->remove_doubles = (lmd->calculation_flags & LRT_REMOVE_DOUBLES) != 0; rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 508061d5d98..b1aa81322a8 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -121,3 +121,13 @@ void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb /* Initial bounding area row/column count, setting 4 is the simplest way algorithm could function * efficiently. */ #define LRT_BA_ROWS 4 + +#ifdef __cplusplus +extern "C" { +#endif + +void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 7c6390971dd..eac33f6c705 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -303,6 +303,14 @@ set(GLSL_SRC shaders/gpu_shader_geometry.glsl + shaders/common/gpu_shader_common_color_ramp.glsl + shaders/common/gpu_shader_common_color_utils.glsl + shaders/common/gpu_shader_common_curves.glsl + shaders/common/gpu_shader_common_hash.glsl + shaders/common/gpu_shader_common_math.glsl + shaders/common/gpu_shader_common_math_utils.glsl + shaders/common/gpu_shader_common_mix_rgb.glsl + shaders/material/gpu_shader_material_add_shader.glsl shaders/material/gpu_shader_material_ambient_occlusion.glsl shaders/material/gpu_shader_material_anisotropic.glsl @@ -315,8 +323,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_bump.glsl shaders/material/gpu_shader_material_camera.glsl shaders/material/gpu_shader_material_clamp.glsl - shaders/material/gpu_shader_material_color_ramp.glsl - shaders/material/gpu_shader_material_color_util.glsl + shaders/material/gpu_shader_material_combine_color.glsl shaders/material/gpu_shader_material_combine_hsv.glsl shaders/material/gpu_shader_material_combine_rgb.glsl shaders/material/gpu_shader_material_combine_xyz.glsl @@ -324,7 +331,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_displacement.glsl shaders/material/gpu_shader_material_eevee_specular.glsl shaders/material/gpu_shader_material_emission.glsl - shaders/material/gpu_shader_material_float_curve.glsl shaders/material/gpu_shader_material_fractal_noise.glsl shaders/material/gpu_shader_material_fresnel.glsl shaders/material/gpu_shader_material_gamma.glsl @@ -333,7 +339,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_glossy.glsl shaders/material/gpu_shader_material_hair_info.glsl shaders/material/gpu_shader_material_hair.glsl - shaders/material/gpu_shader_material_hash.glsl shaders/material/gpu_shader_material_holdout.glsl shaders/material/gpu_shader_material_hue_sat_val.glsl shaders/material/gpu_shader_material_invert.glsl @@ -342,9 +347,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_light_path.glsl shaders/material/gpu_shader_material_mapping.glsl shaders/material/gpu_shader_material_map_range.glsl - shaders/material/gpu_shader_material_math.glsl - shaders/material/gpu_shader_material_math_util.glsl - shaders/material/gpu_shader_material_mix_rgb.glsl shaders/material/gpu_shader_material_mix_shader.glsl shaders/material/gpu_shader_material_noise.glsl shaders/material/gpu_shader_material_normal.glsl @@ -357,8 +359,8 @@ set(GLSL_SRC shaders/material/gpu_shader_material_point_info.glsl shaders/material/gpu_shader_material_principled.glsl shaders/material/gpu_shader_material_refraction.glsl - shaders/material/gpu_shader_material_rgb_curves.glsl shaders/material/gpu_shader_material_rgb_to_bw.glsl + shaders/material/gpu_shader_material_separate_color.glsl shaders/material/gpu_shader_material_separate_hsv.glsl shaders/material/gpu_shader_material_separate_rgb.glsl shaders/material/gpu_shader_material_separate_xyz.glsl @@ -381,10 +383,10 @@ set(GLSL_SRC shaders/material/gpu_shader_material_tex_wave.glsl shaders/material/gpu_shader_material_tex_white_noise.glsl shaders/material/gpu_shader_material_toon.glsl + shaders/material/gpu_shader_material_transform_utils.glsl shaders/material/gpu_shader_material_translucent.glsl shaders/material/gpu_shader_material_transparent.glsl shaders/material/gpu_shader_material_uv_map.glsl - shaders/material/gpu_shader_material_vector_curves.glsl shaders/material/gpu_shader_material_vector_displacement.glsl shaders/material/gpu_shader_material_vector_math.glsl shaders/material/gpu_shader_material_vector_rotate.glsl @@ -496,6 +498,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_2D_widget_info.hh shaders/infos/gpu_shader_3D_depth_only_info.hh shaders/infos/gpu_shader_3D_flat_color_info.hh + shaders/infos/gpu_shader_3D_image_info.hh shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh shaders/infos/gpu_shader_3D_point_info.hh shaders/infos/gpu_shader_3D_polyline_info.hh diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 0d0542aa528..aedac168a01 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -40,6 +40,7 @@ bool GPU_mip_render_workaround(void); bool GPU_depth_blitting_workaround(void); bool GPU_use_main_context_workaround(void); bool GPU_use_hq_normals_workaround(void); +bool GPU_clear_viewport_workaround(void); bool GPU_crappy_amd_driver(void); bool GPU_compute_shader_support(void); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index b620fe9cc9d..3460d33fe68 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -280,6 +280,16 @@ typedef enum eGPUBuiltinShader { GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE, GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR, /** + * Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex. + * + * Exposed via Python-API for add-ons. + * + * \param image: uniform sampler2D + * \param texCoord: in vec2 + * \param pos: in vec3 + */ + GPU_SHADER_3D_IMAGE, + /** * Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex. * * \param alpha: uniform float diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index bb0912f284b..b045d908438 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -280,9 +280,9 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl); /** * Fills the whole texture with the same data for all pixels. * \warning Only work for 2D texture for now. - * \warning Only clears the mip 0 of the texture. + * \warning Only clears the MIP 0 of the texture. * \param data_format: data format of the pixel data. - * \note The format is float for unorm textures. + * \note The format is float for UNORM textures. * \param data: 1 pixel worth of data to fill the texture with. */ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data); diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index c6b0d5902fe..bf8dc409cb7 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -55,7 +55,7 @@ typedef struct GPUVertAttr { /* 1 to 4 or 8 or 12 or 16 */ uint comp_len : 5; /* size in bytes, 1 to 64 */ - uint sz : 7; + uint size : 7; /* from beginning of vertex, in bytes */ uint offset : 11; /* up to GPU_VERT_ATTR_MAX_NAMES */ diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index b750dacaca6..72b62b3de3d 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -142,6 +142,11 @@ bool GPU_use_hq_normals_workaround() return GCaps.use_hq_normals_workaround; } +bool GPU_clear_viewport_workaround() +{ + return GCaps.clear_viewport_workaround; +} + bool GPU_compute_shader_support() { return GCaps.compute_shader_support; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 4a951eb8458..611c2d6973c 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -51,6 +51,7 @@ struct GPUCapabilities { bool use_main_context_workaround = false; bool broken_amd_driver = false; bool use_hq_normals_workaround = false; + bool clear_viewport_workaround = false; /* Vulkan related workarounds. */ /* Metal related workarounds. */ diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index b6b0825a993..f1b46f8bf86 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -32,6 +32,7 @@ #include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ +#include "BLI_vector.hh" #include "gpu_codegen.h" #include "gpu_material_library.h" @@ -58,18 +59,27 @@ struct GPUCodegenCreateInfo : ShaderCreateInfo { /** Duplicate attribute names to avoid reference the GPUNodeGraph directly. */ char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1]; char var_names[16][8]; + blender::Vector<std::array<char, 32>, 16> sampler_names; + + /* Returns the appended name memory location */ + const char *append_sampler_name(const char name[32]) + { + auto index = sampler_names.append_and_get_index(std::array<char, 32>()); + char *name_buffer = sampler_names[index].data(); + memcpy(name_buffer, name, 32); + return name_buffer; + } }; /** Optional generated interface. */ StageInterfaceInfo *interface_generated = nullptr; /** Optional name buffer containing names referenced by StringRefNull. */ - NameBuffer *name_buffer = nullptr; + NameBuffer name_buffer; GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name){}; ~GPUCodegenCreateInfo() { delete interface_generated; - MEM_delete(name_buffer); }; }; @@ -288,7 +298,6 @@ void GPUCodegen::generate_attribs() GPUCodegenCreateInfo &info = *create_info; - info.name_buffer = MEM_new<GPUCodegenCreateInfo::NameBuffer>("info.name_buffer"); info.interface_generated = new StageInterfaceInfo("codegen_iface", "var_attrs"); StageInterfaceInfo &iface = *info.interface_generated; info.vertex_out(iface); @@ -302,11 +311,11 @@ void GPUCodegen::generate_attribs() BLI_assert_msg(0, "Too many attributes"); break; } - STRNCPY(info.name_buffer->attr_names[slot], attr->input_name); - SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id); + STRNCPY(info.name_buffer.attr_names[slot], attr->input_name); + SNPRINTF(info.name_buffer.var_names[slot], "v%d", attr->id); - blender::StringRefNull attr_name = info.name_buffer->attr_names[slot]; - blender::StringRefNull var_name = info.name_buffer->var_names[slot]; + blender::StringRefNull attr_name = info.name_buffer.attr_names[slot]; + blender::StringRefNull var_name = info.name_buffer.var_names[slot]; eGPUType input_type, iface_type; @@ -348,14 +357,19 @@ void GPUCodegen::generate_resources() /* Textures. */ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) { if (tex->colorband) { - info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->sampler_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, name, Frequency::BATCH); } else if (tex->tiled_mapping_name[0] != '\0') { - info.sampler(0, ImageType::FLOAT_2D_ARRAY, tex->sampler_name, Frequency::BATCH); - info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->tiled_mapping_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH); + + const char *name_mapping = info.name_buffer.append_sampler_name(tex->tiled_mapping_name); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, name_mapping, Frequency::BATCH); } else { - info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_2D, name, Frequency::BATCH); } } diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index c62a6416f92..055207eace8 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -50,11 +50,11 @@ void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf) r_name_buf[0] = '\0'; return; } - size_t sz = 0; + size_t len = 0; for (StringRef &name : stack) { - sz += BLI_snprintf_rlen(r_name_buf + sz, name_buf_len - sz, "%s > ", name.data()); + len += BLI_snprintf_rlen(r_name_buf + len, name_buf_len - len, "%s > ", name.data()); } - r_name_buf[sz - 3] = '\0'; + r_name_buf[len - 3] = '\0'; } bool GPU_debug_group_match(const char *ref) diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 7dc1c739750..69467e5b28a 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -490,7 +490,7 @@ static void immEndVertex() /* and move on to the next vertex */ #endif uchar *data = imm->vertex_data + a->offset; - memcpy(data, data - imm->vertex_format.stride, a->sz); + memcpy(data, data - imm->vertex_format.stride, a->size); /* TODO: consolidate copy of adjacent attributes */ } } diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 54473b3a44d..146461d1dfb 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -232,6 +232,7 @@ void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint ma data_ = indices; index_start_ = 0; index_len_ = indices_len; + is_empty_ = min_index > max_index; #if GPU_TRACK_INDEX_RANGE /* Everything remains 32 bit while building to keep things simple. diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh index 9323a81d393..6ce62ae852e 100644 --- a/source/blender/gpu/intern/gpu_index_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh @@ -45,6 +45,8 @@ class IndexBuf { bool is_init_ = false; /** Is this object only a reference to a subrange of another IndexBuf. */ bool is_subrange_ = false; + /** True if buffer only contains restart indices. */ + bool is_empty_ = false; union { /** Mapped buffer data. non-NULL indicates not yet sent to VRAM. */ @@ -61,9 +63,12 @@ class IndexBuf { void init_subrange(IndexBuf *elem_src, uint start, uint length); void init_build_on_device(uint index_len); + /* Returns render index count (not precise). */ uint32_t index_len_get() const { - return index_len_; + /* Return 0 to bypass drawing for index buffers full of restart indices. + * They can lead to graphical glitches on some systems. (See T96892) */ + return is_empty_ ? 0 : index_len_; } /* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */ size_t size_get() const diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index e8fb5fb2c45..7ec6ee5183a 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -168,6 +168,7 @@ static void gpu_material_free_single(GPUMaterial *material) if (material->sss_tex_profile != NULL) { GPU_texture_free(material->sss_tex_profile); } + MEM_freeN(material); } void GPU_material_free(ListBase *gpumaterial) @@ -176,7 +177,6 @@ void GPU_material_free(ListBase *gpumaterial) GPUMaterial *material = link->data; DRW_deferred_shader_remove(material); gpu_material_free_single(material); - MEM_freeN(material); } BLI_freelistN(gpumaterial); } diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index bc7ace792bb..b3a091ffbb0 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -611,7 +611,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) va_start(params, name); for (i = 0; i < function->totparam; i++) { - if (function->paramqual[i] != FUNCTION_QUAL_IN) { + if (function->paramqual[i] == FUNCTION_QUAL_OUT) { linkptr = va_arg(params, GPUNodeLink **); gpu_node_output(node, function->paramtype[i], linkptr); } @@ -669,7 +669,7 @@ static bool gpu_stack_link_v(GPUMaterial *material, } for (i = 0; i < function->totparam; i++) { - if (function->paramqual[i] != FUNCTION_QUAL_IN) { + if (function->paramqual[i] == FUNCTION_QUAL_OUT) { if (totout == 0) { linkptr = va_arg(params, GPUNodeLink **); gpu_node_output(node, function->paramtype[i], linkptr); diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 13238a03688..b92fae4a89b 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -150,6 +150,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .name = "GPU_SHADER_SIMPLE_LIGHTING", .create_info = "gpu_shader_simple_lighting", }, + [GPU_SHADER_3D_IMAGE] = + { + .name = "GPU_SHADER_3D_IMAGE", + .create_info = "gpu_shader_3D_image", + }, [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] = { .name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA", @@ -367,6 +372,16 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, if (sh_cfg == GPU_SHADER_CFG_DEFAULT) { if (stages->create_info != NULL) { *sh_p = GPU_shader_create_from_info_name(stages->create_info); + if (ELEM(shader, + GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR, + GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, + GPU_SHADER_3D_POLYLINE_FLAT_COLOR, + GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR)) { + /* Set a default value for `lineSmooth`. + * Ideally this value should be set by the caller. */ + GPU_shader_bind(*sh_p); + GPU_shader_uniform_1i(*sh_p, "lineSmooth", 1); + } } else { *sh_p = GPU_shader_create_from_arrays_named( diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 460b6d32967..aa2033b9154 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -8,6 +8,7 @@ * shader files. */ +#include <algorithm> #include <iomanip> #include <iostream> #include <sstream> @@ -98,6 +99,10 @@ struct GPUSource { /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */ if (filename.endswith(".h") || filename.endswith(".hh")) { enum_preprocess(); + quote_preprocess(); + } + else { + check_no_quotes(); } if (is_from_material_library()) { @@ -174,6 +179,44 @@ struct GPUSource { } /** + * Some drivers completely forbid quote characters even in unused preprocessor directives. + * We fix the cases where we can't manually patch in `enum_preprocess()`. + * This check ensure none are present in non-patched sources. (see T97545) + */ + void check_no_quotes() + { +#ifdef DEBUG + int64_t pos = -1; + do { + pos = source.find('"', pos + 1); + if (pos == -1) { + break; + } + if (!is_in_comment(source, pos)) { + print_error(source, pos, "Quote characters are forbidden in GLSL files"); + } + } while (true); +#endif + } + + /** + * Some drivers completely forbid string characters even in unused preprocessor directives. + * This fixes the cases we cannot manually patch: Shared headers #includes. (see T97545) + * TODO(fclem): This could be done during the datatoc step. + */ + void quote_preprocess() + { + if (source.find_first_of('"') == -1) { + return; + } + + processed_source = source; + std::replace(processed_source.begin(), processed_source.end(), '"', ' '); + + source = processed_source.c_str(); + } + + /** * Transform C,C++ enum declaration into GLSL compatible defines and constants: * * \code{.cpp} @@ -282,6 +325,7 @@ struct GPUSource { if (last_pos != 0) { output += input.substr(last_pos); } + processed_source = output; source = processed_source.c_str(); }; @@ -549,7 +593,9 @@ struct GPUSource { bool is_from_material_library() const { - return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl"); + return (filename.startswith("gpu_shader_material_") || + filename.startswith("gpu_shader_common_")) && + filename.endswith(".glsl"); } }; diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 2974547c858..13f409cfba5 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -199,7 +199,7 @@ void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void BLI_assert(a_idx < format->attr_len); BLI_assert(verts->data != nullptr); verts->flag |= GPU_VERTBUF_DATA_DIRTY; - memcpy(verts->data + a->offset + v_idx * format->stride, data, a->sz); + memcpy(verts->data + a->offset + v_idx * format->stride, data, a->size); } void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) @@ -208,7 +208,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) const GPUVertFormat *format = &verts->format; BLI_assert(a_idx < format->attr_len); const GPUVertAttr *a = &format->attrs[a_idx]; - const uint stride = a->sz; /* tightly packed input data */ + const uint stride = a->size; /* tightly packed input data */ verts->flag |= GPU_VERTBUF_DATA_DIRTY; GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data); } @@ -235,13 +235,13 @@ void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, c if (format->attr_len == 1 && stride == format->stride) { /* we can copy it all at once */ - memcpy(verts->data, data, vertex_len * a->sz); + memcpy(verts->data, data, vertex_len * a->size); } else { /* we must copy it per vertex */ for (uint v = 0; v < vertex_len; v++) { memcpy( - verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->sz); + verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->size); } } } @@ -256,7 +256,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw verts->flag |= GPU_VERTBUF_DATA_DIRTY; verts->flag &= ~GPU_VERTBUF_DATA_UPLOADED; - access->size = a->sz; + access->size = a->size; access->stride = format->stride; access->data = (uchar *)verts->data + a->offset; access->data_init = access->data; diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index a9ac191754b..59ae862aa51 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -49,7 +49,7 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static uint comp_sz(GPUVertCompType type) +static uint comp_size(GPUVertCompType type) { #if TRUST_NO_ONE assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */ @@ -58,12 +58,12 @@ static uint comp_sz(GPUVertCompType type) return sizes[type]; } -static uint attr_sz(const GPUVertAttr *a) +static uint attr_size(const GPUVertAttr *a) { if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -71,7 +71,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -156,7 +156,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ - attr->sz = attr_sz(attr); + attr->size = attr_size(attr); attr->offset = 0; /* offsets & stride are calculated later (during pack) */ attr->fetch_mode = fetch_mode; @@ -294,13 +294,13 @@ uint padding(uint offset, uint alignment) } #if PACK_DEBUG -static void show_pack(uint a_idx, uint sz, uint pad) +static void show_pack(uint a_idx, uint size, uint pad) { const char c = 'A' + a_idx; for (uint i = 0; i < pad; i++) { putchar('-'); } - for (uint i = 0; i < sz; i++) { + for (uint i = 0; i < size; i++) { putchar(c); } } @@ -310,10 +310,10 @@ void VertexFormat_pack(GPUVertFormat *format) { GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; - uint offset = a0->sz; + uint offset = a0->size; #if PACK_DEBUG - show_pack(0, a0->sz, 0); + show_pack(0, a0->size, 0); #endif for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) { @@ -321,10 +321,10 @@ void VertexFormat_pack(GPUVertFormat *format) uint mid_padding = padding(offset, attr_align(a)); offset += mid_padding; a->offset = offset; - offset += a->sz; + offset += a->size; #if PACK_DEBUG - show_pack(a_idx, a->sz, mid_padding); + show_pack(a_idx, a->size, mid_padding); #endif } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index a0efe12f523..c3118ca320c 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -21,6 +21,7 @@ #include "DNA_userdef_types.h" #include "DNA_vec_types.h" +#include "GPU_capabilities.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_matrix.h" @@ -126,19 +127,23 @@ static void gpu_viewport_textures_create(GPUViewport *viewport) if (viewport->color_render_tx[0] == NULL) { viewport->color_render_tx[0] = GPU_texture_create_2d( "dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); - GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[0] = GPU_texture_create_2d( "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); - GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); + if (GPU_clear_viewport_workaround()) { + GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); + GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); + } } if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) { viewport->color_render_tx[1] = GPU_texture_create_2d( "dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL); - GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[1] = GPU_texture_create_2d( "dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); - GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); + if (GPU_clear_viewport_workaround()) { + GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); + GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); + } } /* Can be shared with GPUOffscreen. */ @@ -262,12 +267,15 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo return; } /* The composite framebuffer object needs to be created in the window context. */ - GPU_framebuffer_ensure_config(&viewport->stereo_comp_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), - GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), - }); + GPU_framebuffer_ensure_config( + &viewport->stereo_comp_fb, + { + GPU_ATTACHMENT_NONE, + /* We need the sRGB attachment to be first for GL_FRAMEBUFFER_SRGB to be turned on. + * Note that this is the opposite of what the texture binding is. */ + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), + }); GPUVertFormat *vert_format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index e08a7835b17..7ec3d390416 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -563,7 +563,7 @@ void gpu::MTLTexture::update_sub( return; } - /* Check Format writeability. */ + /* Check Format write-ability. */ if (mtl_format_get_writeable_view_format(destination_format) == MTLPixelFormatInvalid) { MTL_LOG_ERROR( "[Error]: Updating texture -- destination MTLPixelFormat '%d' does not support write " diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 610fd5d980f..0ce01d80a77 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -419,6 +419,13 @@ static void detect_workarounds() GCaps.shader_storage_buffer_objects_support = false; } + /* Certain Intel based platforms don't clear the viewport textures. Always clearing leads to + * noticeable performance regressions. */ + if (GPU_type_matches( + GPU_DEVICE_INTEL, static_cast<eGPUOSType>(GPU_OS_MAC | GPU_OS_UNIX), GPU_DRIVER_ANY)) { + GCaps.clear_viewport_workaround = true; + } + /* Metal-related Workarounds. */ /* Minimum Per-Vertex stride is 1 byte for OpenGL. */ diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 2dde8d6c86b..e5b879f1f15 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -363,7 +363,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format) } /** - * Assume Unorm / Float target. Used with #glReadPixels. + * Assume UNORM/Float target. Used with #glReadPixels. */ inline GLenum channel_len_to_gl(int channel_len) { diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 04f60f10d41..cfcf77fe705 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -39,8 +39,8 @@ static uint16_t vbo_bind(const ShaderInterface *interface, const GPUVertAttr *a = &format->attrs[a_idx]; if (format->deinterleaved) { - offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len; - stride = a->sz; + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * v_len; + stride = a->size; } else { offset = a->offset; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl index 17240da4050..17240da4050 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl index a5c3a990d90..fe89985ae7f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl @@ -90,6 +90,56 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) outcol = vec4(rgb, hsv.w); } +void rgb_to_hsl(vec4 rgb, out vec4 outcol) +{ + float cmax, cmin, h, s, l; + + cmax = max(rgb[0], max(rgb[1], rgb[2])); + cmin = min(rgb[0], min(rgb[1], rgb[2])); + l = min(1.0, (cmax + cmin) / 2.0); + + if (cmax == cmin) { + h = s = 0.0; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb[0]) { + h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0); + } + else if (cmax == rgb[1]) { + h = (rgb[2] - rgb[0]) / cdelta + 2.0; + } + else { + h = (rgb[0] - rgb[1]) / cdelta + 4.0; + } + } + h /= 6.0; + + outcol = vec4(h, s, l, rgb.w); +} + +void hsl_to_rgb(vec4 hsl, out vec4 outcol) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl[0]; + s = hsl[1]; + l = hsl[2]; + + nr = abs(h * 6.0 - 3.0) - 1.0; + ng = 2.0 - abs(h * 6.0 - 2.0); + nb = 2.0 - abs(h * 6.0 - 4.0); + + nr = clamp(nr, 0.0, 1.0); + nb = clamp(nb, 0.0, 1.0); + ng = clamp(ng, 0.0, 1.0); + + chroma = (1.0 - abs(2.0 * l - 1.0)) * s; + + outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w); +} + void color_alpha_clear(vec4 color, out vec4 result) { result = vec4(color.rgb, 1.0); diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl new file mode 100644 index 00000000000..8948ed77557 --- /dev/null +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -0,0 +1,162 @@ +vec4 white_balance(vec4 color, vec4 black_level, vec4 white_level) +{ + vec4 range = max(white_level - black_level, vec4(1e-5f)); + return (color - black_level) / range; +} + +float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope) +{ + if (parameter < 0.0) { + return value + parameter * start_slope; + } + + if (parameter > 1.0) { + return value + (parameter - 1.0) * end_slope; + } + + return value; +} + +/* Same as extrapolate_if_needed but vectorized. */ +vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 end_slopes) +{ + vec3 end_or_zero_slopes = mix(vec3(0.0), end_slopes, greaterThan(parameters, vec3(1.0))); + vec3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, vec3(0.0))); + parameters = parameters - mix(vec3(0.0), vec3(1.0), greaterThan(parameters, vec3(1.0))); + return values + parameters * slopes; +} + +/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize + * parameters accordingly. */ +#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range) + +void curves_combined_rgb(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + vec4 range_minimums, + vec4 range_dividers, + vec4 start_slopes, + vec4 end_slopes, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the + * UI. */ + vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa); + result.r = texture(curve_map, vec2(parameters.x, layer)).a; + result.g = texture(curve_map, vec2(parameters.y, layer)).a; + result.b = texture(curve_map, vec2(parameters.z, layer)).a; + + /* Then, extrapolate if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa); + + /* Then, evaluate each channel on its curve map. */ + parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb); + result.r = texture(curve_map, vec2(parameters.r, layer)).r; + result.g = texture(curve_map, vec2(parameters.g, layer)).g; + result.b = texture(curve_map, vec2(parameters.b, layer)).b; + + /* Then, extrapolate again if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb); + result.a = color.a; + + result = mix(color, result, factor); +} + +void curves_combined_only(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the + * UI. */ + vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider); + result.r = texture(curve_map, vec2(parameters.x, layer)).a; + result.g = texture(curve_map, vec2(parameters.y, layer)).a; + result.b = texture(curve_map, vec2(parameters.z, layer)).a; + + /* Then, extrapolate if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope)); + result.a = color.a; + + result = mix(color, result, factor); +} + +void curves_vector(vec3 vector, + sampler1DArray curve_map, + const float layer, + vec3 range_minimums, + vec3 range_dividers, + vec3 start_slopes, + vec3 end_slopes, + out vec3 result) +{ + /* Evaluate each component on its curve map. */ + vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers); + result.x = texture(curve_map, vec2(parameters.x, layer)).x; + result.y = texture(curve_map, vec2(parameters.y, layer)).y; + result.z = texture(curve_map, vec2(parameters.z, layer)).z; + + /* Then, extrapolate if needed. */ + result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes); +} + +void curves_vector_mixed(float factor, + vec3 vector, + sampler1DArray curve_map, + const float layer, + vec3 range_minimums, + vec3 range_dividers, + vec3 start_slopes, + vec3 end_slopes, + out vec3 result) +{ + curves_vector( + vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result); + result = mix(vector, result, factor); +} + +void curves_float(float value, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out float result) +{ + /* Evaluate the normalized value on the first curve map. */ + float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider); + result = texture(curve_map, vec2(parameter, layer)).x; + + /* Then, extrapolate if needed. */ + result = extrapolate_if_needed(parameter, result, start_slope, end_slope); +} + +void curves_float_mixed(float factor, + float value, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out float result) +{ + curves_float( + value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result); + result = mix(value, result, factor); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl index 32d61f06a65..32d61f06a65 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl index 0948ce2c9fa..5f640f64056 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void math_add(float a, float b, float c, out float result) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index 91a8996939a..124654963fd 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -139,75 +139,3 @@ mat3 euler_to_mat3(vec3 euler) mat[2][2] = cy * cx; return mat; } - -void normal_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = normal_object_to_world(vin); -} - -void normal_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = normal_world_to_object(vin); -} - -void direction_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrix, vin); -} - -void direction_transform_object_to_view(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrix, vin); - vout = transform_direction(ViewMatrix, vout); -} - -void direction_transform_view_to_world(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrixInverse, vin); -} - -void direction_transform_view_to_object(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrixInverse, vin); - vout = transform_direction(ModelMatrixInverse, vout); -} - -void direction_transform_world_to_view(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrix, vin); -} - -void direction_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrixInverse, vin); -} - -void point_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = point_object_to_world(vin); -} - -void point_transform_object_to_view(vec3 vin, out vec3 vout) -{ - vout = point_object_to_view(vin); -} - -void point_transform_view_to_world(vec3 vin, out vec3 vout) -{ - vout = point_view_to_world(vin); -} - -void point_transform_view_to_object(vec3 vin, out vec3 vout) -{ - vout = point_view_to_object(vin); -} - -void point_transform_world_to_view(vec3 vin, out vec3 vout) -{ - vout = point_world_to_view(vin); -} - -void point_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = point_world_to_object(vin); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index 157a6a27c15..f9652f1150b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 5c97eada77d..6091a5c834a 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -191,8 +191,8 @@ struct GlobalData { vec3 N; /** Geometric Normal. */ vec3 Ng; - /** Surface default Tangent. */ - vec3 T; + /** Curve Tangent Space. */ + vec3 curve_T, curve_B, curve_N; /** Barycentric coordinates. */ vec2 barycentric_coords; vec3 barycentric_dists; diff --git a/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl index 9b1e6fe9d23..522f6de169d 100644 --- a/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl @@ -5,18 +5,6 @@ #define S3D_INTERLACE_COLUMN 1 #define S3D_INTERLACE_CHECKERBOARD 2 -/* Composite stereo textures */ - -#ifndef USE_GPU_SHADER_CREATE_INFO -uniform sampler2D imageTexture; -uniform sampler2D overlayTexture; - -uniform int stereoDisplaySettings; - -layout(location = 0) out vec4 imageColor; -layout(location = 1) out vec4 overlayColor; -#endif - #define stereo_display_mode (stereoDisplaySettings & ((1 << 3) - 1)) #define stereo_interlace_mode ((stereoDisplaySettings >> 3) & ((1 << 3) - 1)) #define stereo_interlace_swap bool(stereoDisplaySettings >> 6) diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh index 4b2d59cc159..3cacd0f4b8d 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh @@ -9,8 +9,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_stereo_merge) .vertex_in(0, Type::VEC2, "pos") - .fragment_out(0, Type::VEC4, "imageColor") - .fragment_out(1, Type::VEC4, "overlayColor") + .fragment_out(0, Type::VEC4, "overlayColor") + .fragment_out(1, Type::VEC4, "imageColor") .sampler(0, ImageType::FLOAT_2D, "imageTexture") .sampler(1, ImageType::FLOAT_2D, "overlayTexture") .push_constant(Type::MAT4, "ModelViewProjectionMatrix") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh index e74e7df1a09..0a4da8d17fe 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh @@ -18,4 +18,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only) GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only_clipped) .additional_info("gpu_shader_3D_depth_only") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh index d23c8985a5d..17d865dbaea 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh @@ -21,4 +21,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color) GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color_clipped) .additional_info("gpu_shader_3D_flat_color") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh new file mode 100644 index 00000000000..94cf58933af --- /dev/null +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "gpu_interface_info.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(gpu_shader_3D_image) + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC2, "texCoord") + .vertex_out(smooth_tex_coord_interp_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .sampler(0, ImageType::FLOAT_2D, "image") + .vertex_source("gpu_shader_3D_image_vert.glsl") + .fragment_source("gpu_shader_image_frag.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh index 1f9c0056f7d..895787e4f1e 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh @@ -41,4 +41,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa) GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa_clipped) .additional_info("gpu_shader_3D_point_uniform_size_uniform_color_aa") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh index a0c21a4ac7c..4cabe110999 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh @@ -21,4 +21,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color) GPU_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color_clipped) .additional_info("gpu_shader_3D_smooth_color") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl index 99117400c57..3f42b6d9094 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl @@ -1,4 +1,4 @@ -void node_add_shader(Closure shader1, Closure shader2, out Closure shader) +void node_add_shader(inout Closure shader1, inout Closure shader2, out Closure shader) { shader = closure_add(shader1, shader2); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl new file mode 100644 index 00000000000..e68d0d98484 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void combine_color_rgb(float r, float g, float b, out vec4 col) +{ + col = vec4(r, g, b, 1.0); +} + +void combine_color_hsv(float h, float s, float v, out vec4 col) +{ + hsv_to_rgb(vec4(h, s, v, 1.0), col); +} + +void combine_color_hsl(float h, float s, float l, out vec4 col) +{ + hsl_to_rgb(vec4(h, s, l, 1.0), col); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl index e8f444080b9..4d9e16afe66 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void combine_hsv(float h, float s, float v, out vec4 col) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl index c81880184e3..530907859e9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl @@ -64,6 +64,8 @@ void node_eevee_specular(vec4 diffuse, else { result = closure_eval(diffuse_data, reflection_data); } - result = closure_add(result, closure_eval(emission_data)); - result = closure_add(result, closure_eval(transparency_data)); + Closure emission_cl = closure_eval(emission_data); + Closure transparency_cl = closure_eval(transparency_data); + result = closure_add(result, emission_cl); + result = closure_add(result, transparency_cl); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl deleted file mode 100644 index 514409f7fdf..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl +++ /dev/null @@ -1,33 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_float_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curve_float(float fac, - float vec, - sampler1DArray curvemap, - float layer, - float range, - vec4 ext, - out float outvec) -{ - float xyz_min = ext.x; - vec = RANGE_RESCALE(vec, xyz_min, range); - - outvec = texture(curvemap, vec2(vec, layer)).x; - - outvec = curve_float_extrapolate(vec, outvec, ext); - - outvec = mix(vec, outvec, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl index 7f502f74c0c..6d52e97cca1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) /* The fractal_noise functions are all exactly the same except for the input type. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl index 29fb09ceebd..64681cd795a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void node_gamma(vec4 col, float gamma, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl index 5e86a4577ee..4c9ff31622f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl @@ -18,7 +18,7 @@ void node_geometry(vec3 orco, true_normal = g_data.Ng; if (g_data.is_strand) { - tangent = g_data.T; + tangent = g_data.curve_T; } else { tangent_orco_z(orco, orco); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl index 7bf8795495a..b24f9ab65f0 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl @@ -40,7 +40,7 @@ void node_bsdf_hair_principled(vec4 color, hair_data.color = color.rgb; hair_data.offset = offset; hair_data.roughness = vec2(0.0); - hair_data.T = g_data.T; + hair_data.T = g_data.curve_B; result = closure_eval(hair_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index a8b4b039370..61458b05c86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -1,18 +1,18 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) void node_hair_info(float hair_length, out float is_strand, out float intercept, out float out_length, out float thickness, - out vec3 tangent, + out vec3 normal, out float random) { is_strand = float(g_data.is_strand); intercept = g_data.hair_time; thickness = g_data.hair_thickness; out_length = hair_length; - tangent = g_data.T; + normal = g_data.curve_N; /* TODO: could be precomputed per strand instead. */ random = wang_hash_noise(uint(g_data.hair_strand_id)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl index 30b808508e9..5223828e176 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl index 119ee3c0eaa..a81e6d36a55 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) float smootherstep(float edge0, float edge1, float x) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl index 312c57231c5..b59257506c9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void mapping_mat4( vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl index c303d21d7c1..00cfba3ca12 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl @@ -1,4 +1,4 @@ -void node_mix_shader(float fac, Closure shader1, Closure shader2, out Closure shader) +void node_mix_shader(float fac, inout Closure shader1, inout Closure shader2, out Closure shader) { shader = closure_mix(shader1, shader2, fac); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl index c84f34a834c..881e38ea11a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) /* clang-format off */ #define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl index ad3d4737193..251103ae57c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) void node_point_info(out vec3 position, out float radius, out float random) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index 033dc05c57d..2e695fa3e14 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -169,6 +169,8 @@ void node_bsdf_principled(vec4 base_color, /* Un-optimized case. */ result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data); } - result = closure_add(result, closure_eval(emission_data)); - result = closure_add(result, closure_eval(transparency_data)); + Closure emission_cl = closure_eval(emission_data); + Closure transparency_cl = closure_eval(transparency_data); + result = closure_add(result, emission_cl); + result = closure_add(result, transparency_cl); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl deleted file mode 100644 index 054fdddf7c3..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl +++ /dev/null @@ -1,73 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curves_rgb(float fac, - vec4 col, - sampler1DArray curvemap, - float layer, - vec4 range, - vec4 ext_r, - vec4 ext_g, - vec4 ext_b, - vec4 ext_a, - out vec4 outcol) -{ - vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer); - vec3 samp; - samp.r = texture(curvemap, co.xw).a; - samp.g = texture(curvemap, co.yw).a; - samp.b = texture(curvemap, co.zw).a; - - samp.r = curve_extrapolate(co.x, samp.r, ext_a); - samp.g = curve_extrapolate(co.y, samp.g, ext_a); - samp.b = curve_extrapolate(co.z, samp.b, ext_a); - - vec3 rgb_min = vec3(ext_r.x, ext_g.x, ext_b.x); - co.xyz = RANGE_RESCALE(samp.rgb, rgb_min, range.rgb); - - samp.r = texture(curvemap, co.xw).r; - samp.g = texture(curvemap, co.yw).g; - samp.b = texture(curvemap, co.zw).b; - - outcol.r = curve_extrapolate(co.x, samp.r, ext_r); - outcol.g = curve_extrapolate(co.y, samp.g, ext_g); - outcol.b = curve_extrapolate(co.z, samp.b, ext_b); - outcol.a = col.a; - - outcol = mix(col, outcol, fac); -} - -void curves_rgb_opti(float fac, - vec4 col, - sampler1DArray curvemap, - float layer, - vec4 range, - vec4 ext_a, - out vec4 outcol) -{ - vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer); - vec3 samp; - samp.r = texture(curvemap, co.xw).a; - samp.g = texture(curvemap, co.yw).a; - samp.b = texture(curvemap, co.zw).a; - - outcol.r = curve_extrapolate(co.x, samp.r, ext_a); - outcol.g = curve_extrapolate(co.y, samp.g, ext_a); - outcol.b = curve_extrapolate(co.z, samp.b, ext_a); - outcol.a = col.a; - - outcol = mix(col, outcol, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl new file mode 100644 index 00000000000..2dd51029cef --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl @@ -0,0 +1,28 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void separate_color_rgb(vec4 col, out float r, out float g, out float b) +{ + r = col.r; + g = col.g; + b = col.b; +} + +void separate_color_hsv(vec4 col, out float r, out float g, out float b) +{ + vec4 hsv; + + rgb_to_hsv(col, hsv); + r = hsv[0]; + g = hsv[1]; + b = hsv[2]; +} + +void separate_color_hsl(vec4 col, out float r, out float g, out float b) +{ + vec4 hsl; + + rgb_to_hsl(col, hsl); + r = hsl[0]; + g = hsl[1]; + b = hsl[2]; +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl index 180e0fd1940..8e475ec39a7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void separate_hsv(vec4 col, out float h, out float s, out float v) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl index edc2fa32177..8d9773913ff 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl @@ -1,5 +1,5 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) vec2 calc_brick_texture(vec3 p, float mortar_size, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl index da131978f72..cf7d6ae18e6 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void node_tex_environment_equirectangular(vec3 co, out vec3 uv) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl index 1552a2facc3..961fe23e67e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) /* 1D Musgrave fBm diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl index c90b2211dcf..3df6f7b29fb 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl index dd12b602edf..0fb8ef15f5f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl @@ -1,5 +1,5 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) /* * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl index eed98232d0b..c24a9417219 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl index 030b58f0736..c5081372aa4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) /* White Noise */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl new file mode 100644 index 00000000000..87048e5c5d6 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl @@ -0,0 +1,71 @@ +void normal_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = normal_object_to_world(vin); +} + +void normal_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = normal_world_to_object(vin); +} + +void direction_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); +} + +void direction_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); + vout = transform_direction(ViewMatrix, vout); +} + +void direction_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); +} + +void direction_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); + vout = transform_direction(ModelMatrixInverse, vout); +} + +void direction_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrix, vin); +} + +void direction_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrixInverse, vin); +} + +void point_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = point_object_to_world(vin); +} + +void point_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = point_object_to_view(vin); +} + +void point_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = point_view_to_world(vin); +} + +void point_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = point_view_to_object(vin); +} + +void point_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = point_world_to_view(vin); +} + +void point_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = point_world_to_object(vin); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl deleted file mode 100644 index f6dec1b24e2..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl +++ /dev/null @@ -1,41 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_vec_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curves_vec(float fac, - vec3 vec, - sampler1DArray curvemap, - float layer, - vec3 range, - vec4 ext_x, - vec4 ext_y, - vec4 ext_z, - out vec3 outvec) -{ - vec4 co = vec4(vec, layer); - - vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x); - co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range); - - outvec.x = texture(curvemap, co.xw).x; - outvec.y = texture(curvemap, co.yw).y; - outvec.z = texture(curvemap, co.zw).z; - - outvec.x = curve_vec_extrapolate(co.x, outvec.r, ext_x); - outvec.y = curve_vec_extrapolate(co.y, outvec.g, ext_y); - outvec.z = curve_vec_extrapolate(co.z, outvec.b, ext_z); - - outvec = mix(vec, outvec, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 8f6bf17f195..018784c42a5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void vector_math_add(vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl index ff0fb1c0418..8f7bd26ca18 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) vec3 rotate_around_axis(vec3 p, vec3 axis, float angle) { diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc index 6ef8a032a73..5dc70a8bf0f 100644 --- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -35,6 +35,7 @@ static void test_shader_builtin() test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 0390df06052..8796c99629e 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -109,6 +109,14 @@ struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[I /** * + * \attention Defined in readimage.c + */ +struct ImBuf *IMB_thumb_load_image(const char *filepath, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE]); + +/** + * * \attention Defined in allocimbuf.c */ void IMB_freeImBuf(struct ImBuf *ibuf); diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 31f8b3a9505..67d1aefeacb 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -36,6 +36,17 @@ typedef struct ImFileType { char colorspace[IM_MAX_SPACE]); /** Load an image from a file. */ struct ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); + /** + * Load/Create a thumbnail image from a filepath. `max_thumb_size` is maximum size of either + * dimension, so can return less on either or both. Should, if possible and performant, return + * dimensions of the full-size image in r_width & r_height. + */ + struct ImBuf *(*load_filepath_thumbnail)(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height); /** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */ bool (*save)(struct ImBuf *ibuf, const char *filepath, int flags); void (*load_tile)(struct ImBuf *ibuf, @@ -143,6 +154,12 @@ struct ImBuf *imb_load_jpeg(const unsigned char *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]); +struct ImBuf *imb_thumbnail_jpeg(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height); /** \} */ diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 096089d4c41..0052ce19aa1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -423,7 +423,7 @@ static int startavi(struct anim *anim) anim->cur_position = 0; # if 0 - printf("x:%d y:%d size:%d interl:%d dur:%d\n", + printf("x:%d y:%d size:%d interlace:%d dur:%d\n", anim->x, anim->y, anim->framesize, diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 548bc9e120c..74042ef75be 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -33,6 +33,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_jpeg, .load = imb_load_jpeg, .load_filepath = NULL, + .load_filepath_thumbnail = imb_thumbnail_jpeg, .save = imb_savejpeg, .load_tile = NULL, .flag = 0, @@ -45,6 +46,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_png, .load = imb_loadpng, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savepng, .load_tile = NULL, .flag = 0, @@ -57,6 +59,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_bmp, .load = imb_bmp_decode, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savebmp, .load_tile = NULL, .flag = 0, @@ -69,6 +72,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_targa, .load = imb_loadtarga, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savetarga, .load_tile = NULL, .flag = 0, @@ -81,6 +85,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_iris, .load = imb_loadiris, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_saveiris, .load_tile = NULL, .flag = 0, @@ -94,6 +99,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_dpx, .load = imb_load_dpx, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_dpx, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -106,6 +112,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_cineon, .load = imb_load_cineon, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_cineon, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -120,6 +127,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_tiff, .load = imb_loadtiff, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savetiff, .load_tile = imb_loadtiletiff, .flag = 0, @@ -134,6 +142,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_hdr, .load = imb_loadhdr, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savehdr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -148,6 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_openexr, .load = imb_load_openexr, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_openexr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -162,6 +172,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_jp2, .load = imb_load_jp2, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_jp2, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -176,6 +187,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_dds, .load = imb_load_dds, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = NULL, .load_tile = NULL, .flag = 0, @@ -190,6 +202,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_photoshop, .load = NULL, .load_filepath = imb_load_photoshop, + .load_filepath_thumbnail = NULL, .save = NULL, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -204,6 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_webp, .load = imb_loadwebp, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savewebp, .load_tile = NULL, .flag = 0, @@ -211,7 +225,7 @@ const ImFileType IMB_FILE_TYPES[] = { .default_save_role = COLOR_ROLE_DEFAULT_BYTE, }, #endif - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, }; const ImFileType *IMB_FILE_TYPES_LAST = &IMB_FILE_TYPES[ARRAY_SIZE(IMB_FILE_TYPES) - 1]; diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 6fb1fb52153..cffa61977f7 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -39,7 +39,11 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes); static void term_source(j_decompress_ptr cinfo); static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size); static boolean handle_app1(j_decompress_ptr cinfo); -static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags); +static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, + int flags, + int max_size, + size_t *r_width, + size_t *r_height); static const uchar jpeg_default_quality = 75; static uchar ibuf_quality; @@ -246,7 +250,11 @@ static boolean handle_app1(j_decompress_ptr cinfo) return true; } -static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags) +static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, + int flags, + int max_size, + size_t *r_width, + size_t *r_height) { JSAMPARRAY row_pointer; JSAMPLE *buffer = NULL; @@ -264,16 +272,34 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla jpeg_save_markers(cinfo, JPEG_COM, 0xffff); if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) { - x = cinfo->image_width; - y = cinfo->image_height; depth = cinfo->num_components; if (cinfo->jpeg_color_space == JCS_YCCK) { cinfo->out_color_space = JCS_CMYK; } + if (r_width) { + *r_width = cinfo->image_width; + } + if (r_height) { + *r_height = cinfo->image_height; + } + + if (max_size > 0) { + /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8, + * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */ + float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height); + cinfo->scale_denom = 8; + cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * (float)cinfo->scale_denom))); + cinfo->dct_method = JDCT_FASTEST; + cinfo->dither_mode = JDITHER_ORDERED; + } + jpeg_start_decompress(cinfo); + x = cinfo->output_width; + y = cinfo->output_height; + if (flags & IB_test) { jpeg_abort_decompress(cinfo); ibuf = IMB_allocImBuf(x, y, 8 * depth, 0); @@ -449,11 +475,92 @@ ImBuf *imb_load_jpeg(const unsigned char *buffer, jpeg_create_decompress(cinfo); memory_source(cinfo, buffer, size); - ibuf = ibJpegImageFromCinfo(cinfo, flags); + ibuf = ibJpegImageFromCinfo(cinfo, flags, -1, NULL, NULL); + + return ibuf; +} + +/* Defines for JPEG Header markers and segment size. */ +#define JPEG_MARKER_MSB (0xFF) +#define JPEG_MARKER_SOI (0xD8) +#define JPEG_MARKER_APP1 (0xE1) +#define JPEG_APP1_MAX (1 << 16) + +struct ImBuf *imb_thumbnail_jpeg(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height) +{ + struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo; + struct my_error_mgr jerr; + FILE *infile = NULL; + + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + + cinfo->err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = jpeg_error; + + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(cinfo); + return NULL; + } + + if ((infile = BLI_fopen(filepath, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", filepath); + return NULL; + } + + /* If file contains an embedded thumbnail, let's return that instead. */ + + if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) && + (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) { + /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */ + unsigned int i = JPEG_APP1_MAX; + /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ + while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) && + !feof(infile) && i--) + ; + if (i > 0 && !feof(infile)) { + /* We found a JPEG thumbnail inside this image. */ + ImBuf *ibuf = NULL; + uchar *buffer = MEM_callocN(JPEG_APP1_MAX, "thumbbuffer"); + /* Just put SOI directly in buffer rather than seeking back 2 bytes. */ + buffer[0] = JPEG_MARKER_MSB; + buffer[1] = JPEG_MARKER_SOI; + if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) { + ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, colorspace); + } + MEM_SAFE_FREE(buffer); + if (ibuf) { + fclose(infile); + return ibuf; + } + } + } + + /* No embedded thumbnail found, so let's create a new one. */ + + fseek(infile, 0, SEEK_SET); + jpeg_create_decompress(cinfo); + + jpeg_stdio_src(cinfo, infile); + ImBuf *ibuf = ibJpegImageFromCinfo(cinfo, flags, max_thumb_size, r_width, r_height); + fclose(infile); return ibuf; } +#undef JPEG_MARKER_MSB +#undef JPEG_MARKER_SOI +#undef JPEG_MARKER_APP1 +#undef JPEG_APP1_MAX + static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) { JSAMPLE *buffer = NULL; diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 9948aaac5da..2281d8d85b3 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1394,12 +1394,10 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; - char tokenbuf[EXR_TOT_MAXNAME]; - int len; /* some multilayers have the combined buffer with names A B G R saved */ if (name[1] == 0) { - echan->chan_id = name[0]; + echan->chan_id = BLI_toupper_ascii(name[0]); layname[0] = '\0'; if (ELEM(name[0], 'R', 'G', 'B', 'A')) { @@ -1416,13 +1414,17 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is channel identifier */ - len = imb_exr_split_token(name, end, &token); + size_t len = imb_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; } + + char channelname[EXR_TOT_MAXNAME]; + BLI_strncpy(channelname, token, std::min(len + 1, sizeof(channelname))); + if (len == 1) { - echan->chan_id = token[0]; + echan->chan_id = BLI_toupper_ascii(channelname[0]); } else if (len > 1) { bool ok = false; @@ -1436,36 +1438,35 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa * * Here we do some magic to distinguish such cases. */ - if (ELEM(token[1], 'X', 'Y', 'Z') || ELEM(token[1], 'R', 'G', 'B') || - ELEM(token[1], 'U', 'V', 'A')) { - echan->chan_id = token[1]; + const char chan_id = BLI_toupper_ascii(channelname[1]); + if (ELEM(chan_id, 'X', 'Y', 'Z', 'R', 'G', 'B', 'U', 'V', 'A')) { + echan->chan_id = chan_id; ok = true; } } - else if (BLI_strcaseeq(token, "red")) { + else if (BLI_strcaseeq(channelname, "red")) { echan->chan_id = 'R'; ok = true; } - else if (BLI_strcaseeq(token, "green")) { + else if (BLI_strcaseeq(channelname, "green")) { echan->chan_id = 'G'; ok = true; } - else if (BLI_strcaseeq(token, "blue")) { + else if (BLI_strcaseeq(channelname, "blue")) { echan->chan_id = 'B'; ok = true; } - else if (BLI_strcaseeq(token, "alpha")) { + else if (BLI_strcaseeq(channelname, "alpha")) { echan->chan_id = 'A'; ok = true; } - else if (BLI_strcaseeq(token, "depth")) { + else if (BLI_strcaseeq(channelname, "depth")) { echan->chan_id = 'Z'; ok = true; } if (ok == false) { - BLI_strncpy(tokenbuf, token, std::min(len + 1, EXR_TOT_MAXNAME)); - printf("multilayer read: unknown channel token: %s\n", tokenbuf); + printf("multilayer read: unknown channel token: %s\n", channelname); return 0; } } diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index df41c0ca757..805a7e8d687 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -22,6 +22,8 @@ #include "IMB_filetype.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_metadata.h" +#include "IMB_thumbs.h" #include "imbuf.h" #include "IMB_colormanagement.h" @@ -234,6 +236,61 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S return ibuf; } +struct ImBuf *IMB_thumb_load_image(const char *filepath, + size_t max_thumb_size, + char colorspace[IM_MAX_SPACE]) +{ + const ImFileType *type = IMB_file_type_from_ftype(IMB_ispic_type(filepath)); + if (type == NULL) { + return NULL; + } + + ImBuf *ibuf = NULL; + int flags = IB_rect | IB_metadata; + /* Size of the original image. */ + size_t width = 0; + size_t height = 0; + + char effective_colorspace[IM_MAX_SPACE] = ""; + if (colorspace) { + BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace)); + } + + if (type->load_filepath_thumbnail) { + ibuf = type->load_filepath_thumbnail( + filepath, flags, max_thumb_size, colorspace, &width, &height); + } + else { + /* Skip images of other types if over 100MB. */ + const size_t file_size = BLI_file_size(filepath); + if (file_size != -1 && file_size > THUMB_SIZE_MAX) { + return NULL; + } + ibuf = IMB_loadiffname(filepath, flags, colorspace); + if (ibuf) { + width = ibuf->x; + height = ibuf->y; + } + } + + if (ibuf) { + imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace); + + if (width > 0 && height > 0) { + /* Save dimensions of original image into the thumbnail metadata. */ + char cwidth[40]; + char cheight[40]; + SNPRINTF(cwidth, "%zu", width); + SNPRINTF(cheight, "%zu", height); + IMB_metadata_ensure(&ibuf->metadata); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Width", cwidth); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Height", cheight); + } + } + + return ibuf; +} + ImBuf *IMB_testiffname(const char *filepath, int flags) { ImBuf *ibuf; diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 37734ebacb2..51951aa9605 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -318,12 +318,8 @@ static ImBuf *thumb_create_ex(const char *file_path, char tpath[FILE_MAX]; char tdir[FILE_MAX]; char temp[FILE_MAX]; - char mtime[40] = "0"; /* in case we can't stat the file */ - char cwidth[40] = "0"; /* in case images have no data */ - char cheight[40] = "0"; + char mtime[40] = "0"; /* in case we can't stat the file */ short tsize = 128; - short ex, ey; - float scaledx, scaledy; BLI_stat_t info; switch (size) { @@ -340,15 +336,6 @@ static ImBuf *thumb_create_ex(const char *file_path, return NULL; /* unknown size */ } - /* exception, skip images over 100mb */ - if (source == THB_SOURCE_IMAGE) { - const size_t file_size = BLI_file_size(file_path); - if (file_size != -1 && file_size > THUMB_SIZE_MAX) { - // printf("file too big: %d, skipping %s\n", (int)size, file_path); - return NULL; - } - } - if (get_thumb_dir(tdir, size)) { BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb); // thumb[8] = '\0'; /* shorten for tempname, not needed anymore */ @@ -368,7 +355,7 @@ static ImBuf *thumb_create_ex(const char *file_path, if (img == NULL) { switch (source) { case THB_SOURCE_IMAGE: - img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL); + img = IMB_thumb_load_image(file_path, tsize, NULL); break; case THB_SOURCE_BLEND: img = IMB_thumb_load_blend(file_path, blen_group, blen_id); @@ -385,8 +372,6 @@ static ImBuf *thumb_create_ex(const char *file_path, if (BLI_stat(file_path, &info) != -1) { BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime); } - BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x); - BLI_snprintf(cheight, sizeof(cheight), "%d", img->y); } } else if (THB_SOURCE_MOVIE == source) { @@ -411,28 +396,20 @@ static ImBuf *thumb_create_ex(const char *file_path, return NULL; } - if (img->x > img->y) { - scaledx = (float)tsize; - scaledy = ((float)img->y / (float)img->x) * tsize; - } - else { - scaledy = (float)tsize; - scaledx = ((float)img->x / (float)img->y) * tsize; - } - /* Scaling down must never assign zero width/height, see: T89868. */ - ex = MAX2(1, (short)scaledx); - ey = MAX2(1, (short)scaledy); - - /* save some time by only scaling byte buf */ - if (img->rect_float) { - if (img->rect == NULL) { - IMB_rect_from_float(img); + if (img->x > tsize || img->y > tsize) { + float scale = MIN2((float)tsize / (float)img->x, (float)tsize / (float)img->y); + /* Scaling down must never assign zero width/height, see: T89868. */ + short ex = MAX2(1, (short)(img->x * scale)); + short ey = MAX2(1, (short)(img->y * scale)); + /* Save some time by only scaling byte buf */ + if (img->rect_float) { + if (img->rect == NULL) { + IMB_rect_from_float(img); + } + imb_freerectfloatImBuf(img); } - - imb_freerectfloatImBuf(img); + IMB_scaleImBuf(img, ex, ey); } - - IMB_scaleImBuf(img, ex, ey); } BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri); IMB_metadata_ensure(&img->metadata); @@ -443,10 +420,6 @@ static ImBuf *thumb_create_ex(const char *file_path, if (use_hash) { IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash); } - if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) { - IMB_metadata_set_field(img->metadata, "Thumb::Image::Width", cwidth); - IMB_metadata_set_field(img->metadata, "Thumb::Image::Height", cheight); - } img->ftype = IMB_FTYPE_PNG; img->planes = 32; diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 5abbc84b0ea..8da9eb9ccf7 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -154,7 +154,7 @@ GPUTexture *IMB_touch_gpu_texture( GPUTexture *tex; if (layers > 0) { - tex = GPU_texture_create_2d_array(name, w, h, layers, 1, tex_format, NULL); + tex = GPU_texture_create_2d_array(name, w, h, layers, 9999, tex_format, NULL); } else { tex = GPU_texture_create_2d(name, w, h, 9999, tex_format, NULL); diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index c1ff7df0c37..edaf53b3efa 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -115,7 +115,7 @@ static void export_startjob(void *customdata, return; } - ABCHierarchyIterator iter(data->depsgraph, abc_archive.get(), data->params); + ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params); if (export_animation) { CLOG_INFO(&LOG, 2, "Exporting animation"); diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index d33adfdb4b9..ab62694b802 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -26,10 +26,11 @@ namespace blender::io::alembic { -ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph, +ABCHierarchyIterator::ABCHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, ABCArchive *abc_archive, const AlembicExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), abc_archive_(abc_archive), params_(params) + : AbstractHierarchyIterator(bmain, depsgraph), abc_archive_(abc_archive), params_(params) { } diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h index 24d3f319fbd..f7814c28a55 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -13,6 +13,7 @@ #include <Alembic/Abc/OObject.h> struct Depsgraph; +struct Main; struct Object; namespace blender::io::alembic { @@ -36,7 +37,8 @@ class ABCHierarchyIterator : public AbstractHierarchyIterator { const AlembicExportParams ¶ms_; public: - ABCHierarchyIterator(Depsgraph *depsgraph, + ABCHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, ABCArchive *abc_archive_, const AlembicExportParams ¶ms); diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index b1add38bf01..e80bd850825 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -8,7 +8,6 @@ set(INC ../../depsgraph ../../makesdna ../../../../intern/guardedalloc - ../../../../extern/fast_float ) set(INC_SYS @@ -19,11 +18,12 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc - intern/string_utils.cc + intern/path_util.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh - IO_string_utils.hh + IO_path_util.hh + IO_path_util_types.h IO_types.h intern/dupli_parent_finder.hh ) @@ -42,7 +42,6 @@ if(WITH_GTESTS) intern/abstract_hierarchy_iterator_test.cc intern/hierarchy_context_order_test.cc intern/object_identifier_test.cc - intern/string_utils_test.cc ) set(TEST_INC ../../blenloader diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 3c6b6cc631e..3371501db95 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -30,6 +30,7 @@ struct Depsgraph; struct DupliObject; struct ID; +struct Main; struct Object; struct ParticleSystem; @@ -204,12 +205,13 @@ class AbstractHierarchyIterator { protected: ExportGraph export_graph_; ExportPathMap duplisource_export_path_; + Main *bmain_; Depsgraph *depsgraph_; WriterMap writers_; ExportSubset export_subset_; public: - explicit AbstractHierarchyIterator(Depsgraph *depsgraph); + explicit AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph); virtual ~AbstractHierarchyIterator(); /* Iterate over the depsgraph, create writers, and tell the writers to write. diff --git a/source/blender/io/common/IO_path_util.hh b/source/blender/io/common/IO_path_util.hh new file mode 100644 index 00000000000..eeb5a9dbcfe --- /dev/null +++ b/source/blender/io/common/IO_path_util.hh @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include "BLI_set.hh" +#include "BLI_string_ref.hh" + +#include "IO_path_util_types.h" + +namespace blender::io { + +/** + * Return a filepath relative to a destination directory, for use with + * exporters. + * + * When PATH_REFERENCE_COPY mode is used, the file path pair (source + * path, destination path) is added to the `copy_set`. + * + * Equivalent of bpy_extras.io_utils.path_reference. + */ +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set = nullptr); + +/** Execute copying files of path_reference. */ +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set); + +} // namespace blender::io diff --git a/source/blender/io/common/IO_path_util_types.h b/source/blender/io/common/IO_path_util_types.h new file mode 100644 index 00000000000..0233f601a81 --- /dev/null +++ b/source/blender/io/common/IO_path_util_types.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +/** Method used to reference paths. Equivalent of bpy_extras.io_utils.path_reference_mode. */ +typedef enum { + /** Use Relative paths with subdirectories only. */ + PATH_REFERENCE_AUTO = 0, + /** Always write absolute paths. */ + PATH_REFERENCE_ABSOLUTE = 1, + /** Write relative paths where possible. */ + PATH_REFERENCE_RELATIVE = 2, + /** Match Absolute/Relative setting with input path. */ + PATH_REFERENCE_MATCH = 3, + /** Filename only. */ + PATH_REFERENCE_STRIP = 4, + /** Copy the file to the destination path. */ + PATH_REFERENCE_COPY = 5, +} ePathReferenceMode; diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index f0501d4cf62..82bb1c57833 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -161,8 +161,8 @@ bool AbstractHierarchyWriter::check_has_deforming_physics(const HierarchyContext return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0; } -AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) - : depsgraph_(depsgraph), export_subset_({true, true}) +AbstractHierarchyIterator::AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph) + : bmain_(bmain), depsgraph_(depsgraph), export_subset_({true, true}) { } diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc index 52cae89c2e8..81a3546259e 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc @@ -54,7 +54,8 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator { used_writers hair_writers; used_writers particle_writers; - explicit TestingHierarchyIterator(Depsgraph *depsgraph) : AbstractHierarchyIterator(depsgraph) + explicit TestingHierarchyIterator(Main *bmain, Depsgraph *depsgraph) + : AbstractHierarchyIterator(bmain, depsgraph) { } ~TestingHierarchyIterator() override @@ -105,7 +106,7 @@ class AbstractHierarchyIteratorTest : public BlendfileLoadingBaseTest { /* Create a test iterator. */ void iterator_create() { - iterator = new TestingHierarchyIterator(depsgraph); + iterator = new TestingHierarchyIterator(bfile->main, depsgraph); } /* Free the test iterator if it is not nullptr. */ void iterator_free() diff --git a/source/blender/io/common/intern/path_util.cc b/source/blender/io/common/intern/path_util.cc new file mode 100644 index 00000000000..902cf552bf0 --- /dev/null +++ b/source/blender/io/common/intern/path_util.cc @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "IO_path_util.hh" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +namespace blender::io { + +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set) +{ + const bool is_relative = BLI_path_is_rel(filepath.c_str()); + char filepath_abs[PATH_MAX]; + BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX); + BLI_path_abs(filepath_abs, base_src.c_str()); + BLI_path_normalize(nullptr, filepath_abs); + + /* Figure out final mode to be used. */ + if (mode == PATH_REFERENCE_MATCH) { + mode = is_relative ? PATH_REFERENCE_RELATIVE : PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_AUTO) { + mode = BLI_path_contains(base_dst.c_str(), filepath_abs) ? PATH_REFERENCE_RELATIVE : + PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_COPY) { + char filepath_cpy[PATH_MAX]; + BLI_path_join( + filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr); + copy_set->add(std::make_pair(filepath_abs, filepath_cpy)); + BLI_strncpy(filepath_abs, filepath_cpy, PATH_MAX); + mode = PATH_REFERENCE_RELATIVE; + } + + /* Now we know the final path mode. */ + if (mode == PATH_REFERENCE_ABSOLUTE) { + return filepath_abs; + } + else if (mode == PATH_REFERENCE_RELATIVE) { + char rel_path[PATH_MAX]; + BLI_strncpy(rel_path, filepath_abs, PATH_MAX); + BLI_path_rel(rel_path, base_dst.c_str()); + /* Can't always find relative path (e.g. between different drives). */ + if (!BLI_path_is_rel(rel_path)) { + return filepath_abs; + } + return rel_path + 2; /* Skip blender's internal "//" prefix. */ + } + else if (mode == PATH_REFERENCE_STRIP) { + return BLI_path_basename(filepath_abs); + } + BLI_assert_msg(false, "Invalid path reference mode"); + return filepath_abs; +} + +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set) +{ + for (const auto © : copy_set) { + const char *src = copy.first.c_str(); + const char *dst = copy.second.c_str(); + if (!BLI_exists(src)) { + fprintf(stderr, "Missing source file '%s', not copying\n", src); + continue; + } + if (0 == BLI_path_cmp_normalized(src, dst)) { + continue; /* Source and dest are the same. */ + } + if (!BLI_make_existing_file(dst)) { + fprintf(stderr, "Can't make directory for '%s', not copying\n", dst); + continue; + } + if (!BLI_copy(src, dst)) { + fprintf(stderr, "Can't copy '%s' to '%s'\n", src, dst); + continue; + } + } +} + +} // namespace blender::io diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index e2e959814fa..7a7c95b29f9 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/usd_writer_mesh.cc intern/usd_writer_metaball.cc intern/usd_writer_transform.cc + intern/usd_writer_volume.cc intern/usd_reader_camera.cc intern/usd_reader_curve.cc @@ -95,6 +96,7 @@ set(SRC intern/usd_writer_mesh.h intern/usd_writer_metaball.h intern/usd_writer_transform.h + intern/usd_writer_volume.h intern/usd_reader_camera.h intern/usd_reader_curve.h @@ -119,8 +121,15 @@ list(APPEND LIB ${BOOST_LIBRARIES} ) -list(APPEND LIB -) +if(WITH_OPENVDB) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) +endif() blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index d7c1a5adc7c..c52d791f3b3 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -97,7 +97,7 @@ static void export_startjob(void *customdata, usd_stage->SetEndTimeCode(scene->r.efra); } - USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); + USDHierarchyIterator iter(data->bmain, data->depsgraph, usd_stage, data->params); if (data->params.export_animation) { /* Writing the animated frames is not 100% of the work, but it's our best guess. */ diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h index 5a3f94d01f8..a636d849296 100644 --- a/source/blender/io/usd/intern/usd_exporter_context.h +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -8,12 +8,14 @@ #include <pxr/usd/usd/common.h> struct Depsgraph; +struct Main; namespace blender::io::usd { class USDHierarchyIterator; struct USDExporterContext { + Main *bmain; Depsgraph *depsgraph; const pxr::UsdStageRefPtr stage; const pxr::SdfPath usd_path; diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index 7a0d896fb3e..51261c4d91e 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -10,6 +10,7 @@ #include "usd_writer_mesh.h" #include "usd_writer_metaball.h" #include "usd_writer_transform.h" +#include "usd_writer_volume.h" #include <string> @@ -28,10 +29,11 @@ namespace blender::io::usd { -USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph, +USDHierarchyIterator::USDHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), stage_(stage), params_(params) + : AbstractHierarchyIterator(bmain, depsgraph), stage_(stage), params_(params) { } @@ -59,6 +61,15 @@ void USDHierarchyIterator::set_export_frame(float frame_nr) export_time_ = pxr::UsdTimeCode(frame_nr); } +std::string USDHierarchyIterator::get_export_file_path() const +{ + /* Returns the same path that was passed to `stage_` object during it's creation (via + * `pxr::UsdStage::CreateNew` function). */ + const pxr::SdfLayerHandle root_layer = stage_->GetRootLayer(); + const std::string usd_export_file_path = root_layer->GetRealPath(); + return usd_export_file_path; +} + const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const { return export_time_; @@ -66,7 +77,8 @@ const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context) { - return USDExporterContext{depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; + return USDExporterContext{ + bmain_, depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; } AbstractHierarchyWriter *USDHierarchyIterator::create_transform_writer( @@ -93,6 +105,9 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch case OB_MBALL: data_writer = new USDMetaballWriter(usd_export_context); break; + case OB_VOLUME: + data_writer = new USDVolumeWriter(usd_export_context); + break; case OB_EMPTY: case OB_CURVES_LEGACY: diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index d5566103af4..445c37238be 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -12,6 +12,7 @@ #include <pxr/usd/usd/timeCode.h> struct Depsgraph; +struct Main; struct Object; namespace blender::io::usd { @@ -27,11 +28,13 @@ class USDHierarchyIterator : public AbstractHierarchyIterator { const USDExportParams ¶ms_; public: - USDHierarchyIterator(Depsgraph *depsgraph, + USDHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams ¶ms); void set_export_frame(float frame_nr); + std::string get_export_file_path() const; const pxr::UsdTimeCode &get_export_time_code() const; virtual std::string make_valid_name(const std::string &name) const override; diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index dbeb9df1ee8..2be9b1c065a 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -47,6 +47,11 @@ bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const return true; } +std::string USDAbstractWriter::get_export_file_path() const +{ + return usd_export_context_.hierarchy_iterator->get_export_file_path(); +} + pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const { if (is_animated_) { diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 66feb6e1070..8adc054c2c2 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -51,6 +51,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter { protected: virtual void do_write(HierarchyContext &context) = 0; + std::string get_export_file_path() const; pxr::UsdTimeCode get_export_time_code() const; pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material); diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 1bfc0e50f69..a24877a20bd 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -576,7 +576,7 @@ static std::string get_tex_image_asset_path(bNode *node, char file_path[FILE_MAX]; BLI_split_file_part(path.c_str(), file_path, FILE_MAX); - if (export_params.relative_texture_paths) { + if (export_params.relative_paths) { BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr); } else { @@ -594,7 +594,7 @@ static std::string get_tex_image_asset_path(bNode *node, return exp_path; } - if (export_params.relative_texture_paths) { + if (export_params.relative_paths) { /* Get the path relative to the USD. */ pxr::SdfLayerHandle layer = stage->GetRootLayer(); std::string stage_path = layer->GetRealPath(); @@ -606,11 +606,7 @@ static std::string get_tex_image_asset_path(bNode *node, strcpy(rel_path, path.c_str()); BLI_path_rel(rel_path, stage_path.c_str()); - - /* BLI_path_rel adds '//' as a prefix to the path, if - * generating the relative path was successful. */ - if (rel_path[0] != '/' || rel_path[1] != '/') { - /* No relative path generated. */ + if (!BLI_path_is_rel(rel_path)) { return path; } diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc new file mode 100644 index 00000000000..b83ded1e5d0 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_volume.cc @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "usd_writer_volume.h" + +#include <pxr/base/tf/pathUtils.h> +#include <pxr/usd/usdVol/openVDBAsset.h> +#include <pxr/usd/usdVol/volume.h> + +#include "DNA_volume_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_volume.h" + +#include "BLI_fileops.h" +#include "BLI_index_range.hh" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "usd_hierarchy_iterator.h" + +namespace blender::io::usd { + +USDVolumeWriter::USDVolumeWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +bool USDVolumeWriter::check_is_animated(const HierarchyContext &context) const +{ + const Volume *volume = static_cast<Volume *>(context.object->data); + return volume->is_sequence; +} + +void USDVolumeWriter::do_write(HierarchyContext &context) +{ + Volume *volume = static_cast<Volume *>(context.object->data); + if (!BKE_volume_load(volume, usd_export_context_.bmain)) { + return; + } + + const int num_grids = BKE_volume_num_grids(volume); + if (!num_grids) { + return; + } + + auto vdb_file_path = resolve_vdb_file(volume); + if (!vdb_file_path.has_value()) { + WM_reportf(RPT_WARNING, + "USD Export: failed to resolve .vdb file for object: %s", + volume->id.name + 2); + return; + } + + if (usd_export_context_.export_params.relative_paths) { + if (auto relative_vdb_file_path = construct_vdb_relative_file_path(vdb_file_path.value())) { + vdb_file_path = relative_vdb_file_path; + } + else { + WM_reportf(RPT_WARNING, + "USD Export: couldn't construct relative file path for .vdb file, absolute path " + "will be used instead"); + } + } + + const pxr::UsdTimeCode timecode = get_export_time_code(); + const pxr::SdfPath &volume_path = usd_export_context_.usd_path; + pxr::UsdStageRefPtr stage = usd_export_context_.stage; + pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path); + + for (const int i : IndexRange(num_grids)) { + const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i); + const std::string grid_name = BKE_volume_grid_name(grid); + const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name); + const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id)); + pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path); + usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode); + usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(vdb_file_path.value()), timecode); + usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path); + } + + float3 volume_bound_min(std::numeric_limits<float>::max()); + float3 volume_bound_max(std::numeric_limits<float>::min()); + if (BKE_volume_min_max(volume, volume_bound_min, volume_bound_max)) { + const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&volume_bound_min[0]), + pxr::GfVec3f(&volume_bound_max[0])}; + usd_volume.GetExtentAttr().Set(volume_extent, timecode); + } + + BKE_volume_unload(volume); +} + +std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const +{ + std::optional<std::string> vdb_file_path; + if (volume->filepath[0] == '\0') { + /* Entering this section should mean that Volume object contains OpenVDB data that is not + * obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to + * Volume' modifier). Try to save this data to a .vdb file. */ + + vdb_file_path = construct_vdb_file_path(volume); + if (!BKE_volume_save( + volume, usd_export_context_.bmain, NULL, vdb_file_path.value_or("").c_str())) { + return std::nullopt; + } + } + + if (!vdb_file_path.has_value()) { + vdb_file_path = BKE_volume_grids_frame_filepath(volume); + if (vdb_file_path.value().empty()) { + return std::nullopt; + } + } + + return vdb_file_path; +} + +std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const +{ + const std::string usd_file_path = get_export_file_path(); + if (usd_file_path.empty()) { + return std::nullopt; + } + + char usd_directory_path[FILE_MAX]; + char usd_file_name[FILE_MAXFILE]; + BLI_split_dirfile(usd_file_path.c_str(), + usd_directory_path, + usd_file_name, + sizeof(usd_directory_path), + sizeof(usd_file_name)); + + if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') { + return std::nullopt; + } + + const char *vdb_directory_name = "volumes"; + + char vdb_directory_path[FILE_MAX]; + BLI_strncpy(vdb_directory_path, usd_directory_path, FILE_MAX); + strcat(vdb_directory_path, vdb_directory_name); + BLI_dir_create_recursive(vdb_directory_path); + + char vdb_file_name[FILE_MAXFILE]; + BLI_strncpy(vdb_file_name, volume->id.name + 2, FILE_MAXFILE); + const pxr::UsdTimeCode timecode = get_export_time_code(); + if (!timecode.IsDefault()) { + const int frame = (int)timecode.GetValue(); + const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame)); + BLI_path_frame(vdb_file_name, frame, num_frame_digits); + } + strcat(vdb_file_name, ".vdb"); + + char vdb_file_path[FILE_MAX]; + BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL); + + return vdb_file_path; +} + +std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path( + const std::string &vdb_file_path) const +{ + const std::string usd_file_path = get_export_file_path(); + if (usd_file_path.empty()) { + return std::nullopt; + } + + char relative_path[FILE_MAX]; + BLI_strncpy(relative_path, vdb_file_path.c_str(), FILE_MAX); + BLI_path_rel(relative_path, usd_file_path.c_str()); + if (!BLI_path_is_rel(relative_path)) { + return std::nullopt; + } + + /* Following code was written with an assumption that Blender's relative paths start with + * // characters as well as have OS dependent slashes. Inside of USD files those relative + * paths should start with either ./ or ../ characters and have always forward slashes (/) + * separating directories. This is the convention used in USD documentation (and it seems + * to be used in other DCC packages as well). */ + std::string relative_path_processed = pxr::TfNormPath(relative_path + 2); + if (relative_path_processed[0] != '.') { + relative_path_processed.insert(0, "./"); + } + + return relative_path_processed; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_volume.h b/source/blender/io/usd/intern/usd_writer_volume.h new file mode 100644 index 00000000000..8c1e36b7e53 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_volume.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include <optional> + +#include "BLI_math_vec_types.hh" +#include "usd_writer_abstract.h" + +struct Volume; + +namespace blender::io::usd { + +/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate .vdb files + * which are referenced in USD file. */ +class USDVolumeWriter : public USDAbstractWriter { + public: + USDVolumeWriter(const USDExporterContext &ctx); + + protected: + virtual bool check_is_animated(const HierarchyContext &context) const override; + virtual void do_write(HierarchyContext &context) override; + + private: + /* Try to ensure that external .vdb file is available for USD to be referenced. Blender can + * either reference external OpenVDB data or generate such data internally. Latter option will + * mean that `resolve_vdb_file` method will try to export volume data to a new .vdb file. If + * successful, this method returns absolute file path to the resolved .vdb file, if not, returns + * `std::nullopt`. */ + std::optional<std::string> resolve_vdb_file(const Volume *volume) const; + + std::optional<std::string> construct_vdb_file_path(const Volume *volume) const; + std::optional<std::string> construct_vdb_relative_file_path( + const std::string &vdb_file_path) const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index e63cd0a4e04..deaa27a5252 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -28,7 +28,7 @@ struct USDExportParams { bool generate_preview_surface; bool export_textures; bool overwrite_textures; - bool relative_texture_paths; + bool relative_paths; }; struct USDImportParams { diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index 67cec000778..f7958ef4ec6 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../makesrna ../../nodes ../../windowmanager + ../../../../extern/fast_float ../../../../extern/fmtlib/include ../../../../intern/guardedalloc ) @@ -35,6 +36,7 @@ set(SRC importer/obj_import_mesh.cc importer/obj_import_mtl.cc importer/obj_import_nurbs.cc + importer/obj_import_string_utils.cc importer/obj_importer.cc IO_wavefront_obj.h @@ -50,6 +52,7 @@ set(SRC importer/obj_import_mtl.hh importer/obj_import_nurbs.hh importer/obj_import_objects.hh + importer/obj_import_string_utils.hh importer/obj_importer.hh ) @@ -69,8 +72,10 @@ blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/obj_exporter_tests.cc + tests/obj_import_string_utils_tests.cc tests/obj_importer_tests.cc tests/obj_mtl_parser_tests.cc + tests/obj_exporter_tests.hh ) diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 8b71ec750c0..f7bf678310f 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -9,6 +9,7 @@ #include "BKE_context.h" #include "BLI_path_util.h" #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #ifdef __cplusplus extern "C" { @@ -37,6 +38,8 @@ static const int TOTAL_AXES = 3; struct OBJExportParams { /** Full path to the destination .OBJ file. */ char filepath[FILE_MAX]; + /** Pretend that destination file folder is this, if non-empty. Used only for tests. */ + char file_base_for_tests[FILE_MAX]; /** Full path to current blender file (used for comments in output). */ const char *blen_filepath; @@ -62,6 +65,7 @@ struct OBJExportParams { bool export_materials; bool export_triangulated_mesh; bool export_curves_as_nurbs; + ePathReferenceMode path_mode; /* Grouping options. */ bool export_object_groups; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 194583e71fe..b027df73b1e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -13,6 +13,8 @@ #include "BLI_path_util.h" #include "BLI_task.hh" +#include "IO_path_util.hh" + #include "obj_export_mesh.hh" #include "obj_export_mtl.hh" #include "obj_export_nurbs.hh" @@ -530,7 +532,11 @@ void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material) void MTLWriter::write_texture_map( const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map) + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode path_mode, + Set<std::pair<std::string, std::string>> ©_set) { std::string options; /* Option strings should have their own leading spaces. */ @@ -546,7 +552,11 @@ void MTLWriter::write_texture_map( #define SYNTAX_DISPATCH(eMTLSyntaxElement) \ if (texture_map.key == eMTLSyntaxElement) { \ - fmt_handler_.write<eMTLSyntaxElement>(options, texture_map.value.image_path); \ + std::string path = path_reference( \ + texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set); \ + /* Always emit forward slashes for cross-platform compatibility. */ \ + std::replace(path.begin(), path.end(), '\\', '/'); \ + fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \ return; \ } @@ -561,25 +571,35 @@ void MTLWriter::write_texture_map( BLI_assert(!"This map type was not written to the file."); } -void MTLWriter::write_materials() +void MTLWriter::write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir) { if (mtlmaterials_.size() == 0) { return; } + + char blen_filedir[PATH_MAX]; + BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX); + BLI_path_slash_native(blen_filedir); + BLI_path_normalize(nullptr, blen_filedir); + std::sort(mtlmaterials_.begin(), mtlmaterials_.end(), [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; }); + Set<std::pair<std::string, std::string>> copy_set; for (const MTLMaterial &mtlmat : mtlmaterials_) { fmt_handler_.write<eMTLSyntaxElement::string>("\n"); fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name); write_bsdf_properties(mtlmat); - for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map : - mtlmat.texture_maps.items()) { - if (!texture_map.value.image_path.empty()) { - write_texture_map(mtlmat, texture_map); + for (const auto &tex : mtlmat.texture_maps.items()) { + if (tex.value.image_path.empty()) { + continue; } + write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set); } } + path_reference_copy(copy_set); } Vector<int> MTLWriter::add_materials(const OBJMesh &mesh_to_export) diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 96f7d434338..77da7b44276 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -9,6 +9,7 @@ #include "DNA_meshdata_types.h" #include "BLI_map.hh" +#include "BLI_set.hh" #include "BLI_vector.hh" #include "IO_wavefront_obj.h" @@ -181,7 +182,9 @@ class MTLWriter : NonMovable, NonCopyable { * For consistency of output from run to run (useful for testing), * the materials are sorted by name before writing. */ - void write_materials(); + void write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir); StringRefNull mtl_file_path() const; /** * Add the materials of the given object to #MTLWriter, de-duplicating @@ -203,6 +206,10 @@ class MTLWriter : NonMovable, NonCopyable { * Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image". */ void write_texture_map(const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map); + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> ©_set); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index c48d5a5f7f0..4ed148ec64e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -113,8 +113,7 @@ static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> socket /** * From a texture image shader node, get the image's filepath. - * Returned filepath is stripped of initial "//". If packed image is found, - * only the file "name" is returned. + * If packed image is found, only the file "name" is returned. */ static const char *get_image_filepath(const bNode *tex_node) { @@ -134,9 +133,6 @@ static const char *get_image_filepath(const bNode *tex_node) "directory as the .MTL file.\n", path); } - if (path[0] == '/' && path[1] == '/') { - path += 2; - } return path; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 78b709c884a..b6e636b389d 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -284,7 +284,16 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params); if (mtl_writer) { mtl_writer->write_header(export_params.blen_filepath); - mtl_writer->write_materials(); + char dest_dir[PATH_MAX]; + if (export_params.file_base_for_tests[0] == '\0') { + BLI_split_dir_part(export_params.filepath, dest_dir, PATH_MAX); + } + else { + BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX); + } + BLI_path_slash_native(dest_dir); + BLI_path_normalize(nullptr, dest_dir); + mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir); } write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer); } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index f88b3e143a6..fa89b49b605 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -8,9 +8,8 @@ #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "IO_string_utils.hh" - #include "obj_import_file_reader.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { @@ -128,6 +127,7 @@ static void geom_add_polygon(Geometry *geom, curr_face.start_index_ = orig_corners_size; bool face_valid = true; + line = drop_whitespace(line); while (!line.is_empty() && face_valid) { PolyCorner corner; bool got_uv = false, got_normal = false; @@ -399,6 +399,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, StringRef buffer_str{buffer.data(), (int64_t)last_nl}; while (!buffer_str.is_empty()) { StringRef line = read_next_line(buffer_str); + line = drop_whitespace(line); ++line_number; if (line.is_empty()) { continue; @@ -484,9 +485,6 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, else if (line.startswith("end")) { /* End of curve definition, nothing else to do. */ } - else if (line.front() <= ' ') { - /* Just whitespace, skip. */ - } else { std::cout << "OBJ element not recognized: '" << line << "'" << std::endl; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index c2ecd8a37de..f39def0a4af 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -13,13 +13,12 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" -#include "IO_string_utils.hh" - #include "NOD_shader.h" /* TODO: move eMTLSyntaxElement out of following file into a more neutral place */ #include "obj_export_io.hh" #include "obj_import_mtl.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index 3a12250e14b..c60306c8375 100644 --- a/source/blender/io/common/intern/string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "IO_string_utils.hh" +#include "obj_import_string_utils.hh" /* Note: we could use C++17 <charconv> from_chars to parse * floats, but even if some compilers claim full support, @@ -11,7 +11,7 @@ #include "fast_float.h" #include <charconv> -namespace blender::io { +namespace blender::io::obj { StringRef read_next_line(StringRef &buffer) { @@ -96,4 +96,4 @@ StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space) return StringRef(res.ptr, str.end()); } -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/io/common/IO_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh index 25f1f01c6ed..532224569ac 100644 --- a/source/blender/io/common/IO_string_utils.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -5,10 +5,13 @@ #include "BLI_string_ref.hh" /* - * Various text parsing utilities commonly used by text-based input formats. + * Various text parsing utilities used by OBJ importer. + * The utilities are not directly usable by other formats, since + * they treat backslash (\) as a whitespace character (OBJ format + * allows backslashes to function as a line-continuation character). */ -namespace blender::io { +namespace blender::io::obj { /** * Fetches next line from an input string buffer. @@ -18,7 +21,7 @@ namespace blender::io { * the input line. * * Note that backslash (\) character is treated as a line - * continuation, similar to OBJ file format or a C preprocessor. + * continuation. */ StringRef read_next_line(StringRef &buffer); @@ -66,4 +69,4 @@ StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space */ StringRef parse_floats(StringRef str, float fallback, float *dst, int count); -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index f74bfc155dd..8c49af90a82 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -11,12 +11,15 @@ #include "BKE_appdir.h" #include "BKE_blender_version.h" +#include "BKE_main.h" #include "BLI_fileops.h" #include "BLI_index_range.hh" #include "BLI_string_utf8.h" #include "BLI_vector.hh" +#include "BLO_readfile.h" + #include "DEG_depsgraph.h" #include "obj_export_file_writer.hh" @@ -259,11 +262,12 @@ class obj_exporter_regression_test : public obj_exporter_test { std::string tempdir = std::string(BKE_tempdir_base()); std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str()); strncpy(params.filepath, out_file_path.c_str(), FILE_MAX - 1); - params.blen_filepath = blendfile.c_str(); + params.blen_filepath = bfile->main->filepath; + std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; + BLI_split_dir_part(golden_file_path.c_str(), params.file_base_for_tests, PATH_MAX); export_frame(depsgraph, params, out_file_path.c_str()); std::string output_str = read_temp_file_in_string(out_file_path); - std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; std::string golden_str = read_temp_file_in_string(golden_file_path); bool are_equal = strings_equal_after_first_lines(output_str, golden_str); if (save_failing_test_output && !are_equal) { @@ -432,19 +436,26 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) _export.params); } -/* Note: texture paths in the resulting mtl file currently are always - * as they are stored in the source .blend file; not relative to where - * the export is done. When that is properly fixed, the expected .mtl - * file should be updated. */ -TEST_F(obj_exporter_regression_test, cubes_with_textures) +TEST_F(obj_exporter_regression_test, cubes_with_textures_strip) { OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_STRIP; compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", "io_tests/obj/cubes_with_textures.obj", "io_tests/obj/cubes_with_textures.mtl", _export.params); } +TEST_F(obj_exporter_regression_test, cubes_with_textures_relative) +{ + OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_RELATIVE; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", + "io_tests/obj/cubes_with_textures_rel.obj", + "io_tests/obj/cubes_with_textures_rel.mtl", + _export.params); +} + TEST_F(obj_exporter_regression_test, suzanne_all_data) { OBJExportParamsDefault _export; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 6a821e0b1bf..ef27a65fb4b 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -11,6 +11,7 @@ struct OBJExportParamsDefault { OBJExportParamsDefault() { params.filepath[0] = '\0'; + params.file_base_for_tests[0] = '\0'; params.blen_filepath = ""; params.export_animation = false; params.start_frame = 0; @@ -26,6 +27,7 @@ struct OBJExportParamsDefault { params.export_uv = true; params.export_normals = true; params.export_materials = true; + params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; params.export_curves_as_nurbs = false; diff --git a/source/blender/io/common/intern/string_utils_test.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc index a78bd7ab8a3..addb1fa473e 100644 --- a/source/blender/io/common/intern/string_utils_test.cc +++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc @@ -1,14 +1,14 @@ /* SPDX-License-Identifier: Apache-2.0 */ -#include "IO_string_utils.hh" +#include "obj_import_string_utils.hh" #include "testing/testing.h" -namespace blender::io { +namespace blender::io::obj { #define EXPECT_STRREF_EQ(str1, str2) EXPECT_STREQ(str1, std::string(str2).c_str()) -TEST(string_utils, read_next_line) +TEST(obj_import_string_utils, read_next_line) { std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; StringRef s = str; @@ -21,7 +21,7 @@ TEST(string_utils, read_next_line) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, drop_whitespace) +TEST(obj_import_string_utils, drop_whitespace) { /* Empty */ EXPECT_STRREF_EQ("", drop_whitespace("")); @@ -39,7 +39,7 @@ TEST(string_utils, drop_whitespace) EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); } -TEST(string_utils, parse_int_valid) +TEST(obj_import_string_utils, parse_int_valid) { std::string str = "1 -10 \t 1234 1234567890 +7 123a"; StringRef s = str; @@ -59,7 +59,7 @@ TEST(string_utils, parse_int_valid) EXPECT_STRREF_EQ("a", s); } -TEST(string_utils, parse_int_invalid) +TEST(obj_import_string_utils, parse_int_invalid) { int val; /* Invalid syntax */ @@ -75,7 +75,7 @@ TEST(string_utils, parse_int_invalid) EXPECT_EQ(val, -4); } -TEST(string_utils, parse_float_valid) +TEST(obj_import_string_utils, parse_float_valid) { std::string str = "1 -10 123.5 -17.125 0.1 1e6 50.0e-1"; StringRef s = str; @@ -97,7 +97,7 @@ TEST(string_utils, parse_float_valid) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, parse_float_invalid) +TEST(obj_import_string_utils, parse_float_invalid) { float val; /* Invalid syntax */ @@ -115,4 +115,4 @@ TEST(string_utils, parse_float_invalid) EXPECT_EQ(val, -4.0f); } -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 99737aa3b67..c1dfab8a041 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -304,7 +304,7 @@ typedef struct DriverTarget { char *rna_path; /** - * Name of the posebone to use + * Name of the pose-bone to use * (for vars where DTAR_FLAG_STRUCT_REF is used) - `MAX_ID_NAME - 2`. */ char pchan_name[64]; @@ -918,9 +918,9 @@ typedef struct KS_Path { /** Index that path affects. */ int array_index; - /** (eInsertKeyFlags) settings to supply insertkey() with. */ + /** (#eInsertKeyFlags) settings to supply insert-key() with. */ short keyingflag; - /** (eInsertKeyFlags) for each flag set, the relevant keyingflag bit overrides the default. */ + /** (#eInsertKeyFlags) for each flag set, the relevant keying-flag bit overrides the default. */ short keyingoverride; } KS_Path; diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 1d5f1351de0..f409d1c0442 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -618,6 +618,7 @@ typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_GROW_SHRINK_INVERT = (1 << 1), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 2), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 3), + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT = (1 << 4), } eBrushCurvesSculptFlag; #define MAX_BRUSH_PIXEL_RADIUS 500 diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 7d230b7d7a3..24e77ecf87f 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -140,6 +140,8 @@ typedef struct BrushGpencilSettings { typedef struct BrushCurvesSculptSettings { /** Number of curves added by the add brush. */ int add_amount; + /** Number of control points in new curves added by the add brush. */ + int points_per_curve; /* eBrushCurvesSculptFlag. */ uint32_t flag; /** When shrinking curves, they shouldn't become shorter than this length. */ diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index e909abc70cb..2388f04cc39 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -89,6 +89,9 @@ typedef struct CurvesGeometry { * this array is allocated with a length one larger than the number of curves. This is allowed * to be null when there are no curves. * + * Every curve offset must be at least one larger than the previous. + * In other words, every curve must have at least one point. + * * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. */ int *curve_offsets; @@ -107,11 +110,11 @@ typedef struct CurvesGeometry { /** * The total number of control points in all curves. */ - int point_size; + int point_num; /** * The number of curves in the data-block. */ - int curve_size; + int curve_num; /** * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 4a1b639122a..a83262d7639 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -245,7 +245,7 @@ typedef struct bGPDstroke_Runtime { /** Vertex offset in the VBO where this stroke starts. */ int stroke_start; - /** Triangle offset in the ibo where this fill starts. */ + /** Triangle offset in the IBO where this fill starts. */ int fill_start; /** Curve Handles offset in the IBO where this handle starts. */ int curve_start; @@ -814,10 +814,10 @@ typedef enum eGPdata_Flag { /* Vertex Paint Mode - Toggle paint mode */ GP_DATA_STROKE_VERTEXMODE = (1 << 18), - /* Autolock not active layers */ + /* Auto-lock not active layers. */ GP_DATA_AUTOLOCK_LAYERS = (1 << 20), - /* Enable Bezier Editing Curve (a submode of Edit mode). */ + /* Enable Bezier Editing Curve (a sub-mode of Edit mode). */ GP_DATA_CURVE_EDIT_MODE = (1 << 21), /* Use adaptive curve resolution */ GP_DATA_CURVE_ADAPTIVE_RESOLUTION = (1 << 22), diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index 2e446427cc3..39b75b6eda2 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -21,7 +21,7 @@ typedef enum eLineartMainFlags { LRT_ALLOW_DUPLI_OBJECTS = (1 << 2), LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3), LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4), - LRT_REMOVE_DOUBLES = (1 << 5), + /* LRT_REMOVE_DOUBLES = (1 << 5), Deprecated */ LRT_LOOSE_AS_CONTOUR = (1 << 6), LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 7), LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 8), @@ -47,9 +47,16 @@ typedef enum eLineartEdgeFlag { LRT_EDGE_FLAG_MATERIAL = (1 << 3), LRT_EDGE_FLAG_INTERSECTION = (1 << 4), LRT_EDGE_FLAG_LOOSE = (1 << 5), - LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 6), - LRT_EDGE_FLAG_CLIPPED = (1 << 7), - /** Limited to 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */ + /* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */ + /* Limited to 8 bits for edge type flag, don't add anymore because BMEdge->head.eflag only has 8 + bits. So unless we changed this into a non-single-bit flag thing, we keep it this way. */ + /** Also used as discarded line mark. */ + LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 8), + LRT_EDGE_FLAG_CLIPPED = (1 << 9), + /** Limited to 16 bits for the entire thing. */ + + /** For object loading code to use only. */ + LRT_EDGE_FLAG_INHIBIT = (1 << 14), } eLineartEdgeFlag; #define LRT_EDGE_FLAG_ALL_TYPE 0x3f diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index b98c9dd2886..97e057361c1 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -280,7 +280,7 @@ typedef struct Mesh { /** Various flags used when editing the mesh. */ char editflag; /** Mostly more flags used when editing or displaying the mesh. */ - short flag; + unsigned short flag; /** * The angle for auto smooth in radians. `M_PI` (180 degrees) causes all edges to be smooth. diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index d18fe1b81dd..92c4d8fe938 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -855,6 +855,12 @@ typedef struct NodeVertexCol { char name[64]; } NodeVertexCol; +typedef struct NodeCMPCombSepColor { + /* CMPNodeCombSepColorMode */ + uint8_t mode; + uint8_t ycc_mode; +} NodeCMPCombSepColor; + /** Defocus blur node. */ typedef struct NodeDefocus { char bktype, _pad0, preview, gamco; @@ -1485,6 +1491,11 @@ typedef struct NodeFunctionCompare { char _pad[1]; } NodeFunctionCompare; +typedef struct NodeCombSepColor { + /* NodeCombSepColorMode */ + int8_t mode; +} NodeCombSepColor; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1877,6 +1888,16 @@ typedef enum CMPNodeDenoisePrefilter { CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2 } CMPNodeDenoisePrefilter; +/* Color combine/separate modes */ + +typedef enum CMPNodeCombSepColorMode { + CMP_NODE_COMBSEP_COLOR_RGB = 0, + CMP_NODE_COMBSEP_COLOR_HSV = 1, + CMP_NODE_COMBSEP_COLOR_HSL = 2, + CMP_NODE_COMBSEP_COLOR_YCC = 3, + CMP_NODE_COMBSEP_COLOR_YUV = 4, +} CMPNodeCombSepColorMode; + #define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 /* Point Density shader node */ @@ -2135,6 +2156,12 @@ typedef enum GeometryNodeScaleElementsMode { GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1, } GeometryNodeScaleElementsMode; +typedef enum NodeCombSepColorMode { + NODE_COMBSEP_COLOR_RGB = 0, + NODE_COMBSEP_COLOR_HSV = 1, + NODE_COMBSEP_COLOR_HSL = 2, +} NodeCombSepColorMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index c1e96bcfaf3..8e29d88a583 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1223,7 +1223,8 @@ typedef struct SpaceImage { char dt_uvstretch; char around; - char _pad1[4]; + char gizmo_flag; + char _pad1[3]; int flag; @@ -1321,6 +1322,13 @@ typedef enum eSpaceImageOverlay_Flag { SI_OVERLAY_SHOW_GRID_BACKGROUND = (1 << 1), } eSpaceImageOverlay_Flag; +/** #SpaceImage.gizmo_flag */ +enum { + /** All gizmos. */ + SI_GIZMO_HIDE = (1 << 0), + SI_GIZMO_HIDE_NAVIGATE = (1 << 1), +}; + /** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */ #define SI_GRID_STEPS_LEN 8 @@ -1332,7 +1340,7 @@ typedef enum eSpaceImageOverlay_Flag { typedef struct SpaceText_Runtime { - /** Actual line height, scaled by dpi. */ + /** Actual line height, scaled by DPI. */ int lheight_px; /** Runtime computed, character width. */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 4d6e0eba915..275a89ec680 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -57,7 +57,7 @@ typedef struct uiFontStyle { /** Saved in file, 0 is default. */ short uifont_id; char _pad1[2]; - /** Actual size depends on 'global' dpi. */ + /** Actual size depends on 'global' DPI. */ float points; /** Style hint. */ short italic, bold; @@ -652,7 +652,7 @@ typedef struct UserDef_Experimental { char use_override_templates; char enable_eevee_next; char use_sculpt_texture_paint; - char _pad0[1]; + char use_draw_manager_acquire_lock; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 281aeae7a60..f25ff5fbbb8 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -61,6 +61,8 @@ DNA_STRUCT_RENAME_ELEM(Curve, ext1, extrude) DNA_STRUCT_RENAME_ELEM(Curve, ext2, bevel_radius) DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Curve, width, offset) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, curve_size, curve_num) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, point_size, point_num) DNA_STRUCT_RENAME_ELEM(CustomDataExternal, filename, filepath) DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b65e08311fe..b0488bbfa7a 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1976,7 +1976,7 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Extra User", - "Indicates wether an extra user is set or not (mainly for internal/debug usages)"); + "Indicates whether an extra user is set or not (mainly for internal/debug usages)"); RNA_def_property_boolean_funcs(prop, NULL, "rna_ID_extra_user_set"); prop = RNA_def_property(srna, "is_embedded_data", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index c42cbdbbb56..9161fee2584 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1953,6 +1953,11 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_range(prop, 1, INT32_MAX); RNA_def_property_ui_text(prop, "Add Amount", "Number of curves added by the Add brush"); + prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 2, INT32_MAX); + RNA_def_property_ui_text( + prop, "Points per Curve", "Number of control points in a newly added curve"); + prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); RNA_def_property_ui_text(prop, @@ -1970,6 +1975,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT); + RNA_def_property_ui_text(prop, + "Interpolate Point Count", + "Use the number of points from the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 2dc568d0b8a..60530eb8bf9 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -38,7 +38,7 @@ static Curves *rna_curves(PointerRNA *ptr) static int rna_Curves_curve_offset_data_length(PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); - return curves->geometry.curve_size + 1; + return curves->geometry.curve_num + 1; } static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -47,7 +47,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, rna_iterator_array_begin(iter, (void *)curves->geometry.curve_offsets, sizeof(int), - curves->geometry.curve_size + 1, + curves->geometry.curve_num + 1, false, NULL); } @@ -222,7 +222,7 @@ static void rna_def_curves(BlenderRNA *brna) /* Point and Curve RNA API helpers. */ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_num"); RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); @@ -230,7 +230,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "CurvePoint"); RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); RNA_define_verify_sdna(1); @@ -239,7 +239,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); RNA_def_property_update(prop, 0, "rna_Curves_update_data"); RNA_define_verify_sdna(1); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index da2c404ac88..05e8d5406b4 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -233,8 +233,8 @@ static const EnumPropertyItem gpencil_envelope_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem modifier_noise_random_mode_items[] = { - {GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Apply random every N steps"}, - {GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "On Keyframes", "Apply random every keyframe"}, + {GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Randomize every number of frames"}, + {GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "Keyframes", "Randomize on keyframes only"}, {0, NULL, 0, NULL, NULL}, }; #endif @@ -929,8 +929,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "step"); RNA_def_property_range(prop, 1, 100); - RNA_def_property_ui_text( - prop, "Step", "Number of frames before recalculate random values again"); + RNA_def_property_ui_text(prop, "Step", "Number of frames interval between randomization steps"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); @@ -967,7 +966,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop = RNA_def_property(srna, "random_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "noise_mode"); RNA_def_property_enum_items(prop, modifier_noise_random_mode_items); - RNA_def_property_ui_text(prop, "Mode", "How the random changes are applied"); + RNA_def_property_ui_text(prop, "Mode", "Where to perform randomization"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); RNA_define_lib_overridable(false); @@ -3270,12 +3269,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 30.0f); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_remove_doubles", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES); - RNA_def_property_ui_text( - prop, "Remove Doubles", "Remove doubles from the source geometry before generating stokes"); - RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_loose_as_contour", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_LOOSE_AS_CONTOUR); RNA_def_property_ui_text(prop, "Loose As Contour", "Loose edges will have contour type"); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 2c2832e63da..6c225019441 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -1073,22 +1073,31 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Depth", "Image bit depth"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - prop = RNA_def_int_vector(srna, - "size", - 2, - NULL, - 0, - 0, - "Size", - "Width and height in pixels, zero when image data can't be loaded", - 0, - 0); + prop = RNA_def_int_vector( + srna, + "size", + 2, + NULL, + 0, + 0, + "Size", + "Width and height of the image buffer in pixels, zero when image data can't be loaded", + 0, + 0); RNA_def_property_subtype(prop, PROP_PIXEL); RNA_def_property_int_funcs(prop, "rna_Image_size_get", NULL, NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - prop = RNA_def_float_vector( - srna, "resolution", 2, NULL, 0, 0, "Resolution", "X/Y pixels per meter", 0, 0); + prop = RNA_def_float_vector(srna, + "resolution", + 2, + NULL, + 0, + 0, + "Resolution", + "X/Y pixels per meter, for the image buffer", + 0, + 0); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, "rna_Image_resolution_get", "rna_Image_resolution_set", NULL); @@ -1105,7 +1114,7 @@ static void rna_def_image(BlenderRNA *brna) prop = RNA_def_property(srna, "pixels", PROP_FLOAT, PROP_NONE); RNA_def_property_flag(prop, PROP_DYNAMIC); RNA_def_property_multi_array(prop, 1, NULL); - RNA_def_property_ui_text(prop, "Pixels", "Image pixels in floating-point values"); + RNA_def_property_ui_text(prop, "Pixels", "Image buffer pixels in floating-point values"); RNA_def_property_dynamic_array_funcs(prop, "rna_Image_pixels_get_length"); RNA_def_property_float_funcs(prop, "rna_Image_pixels_get", "rna_Image_pixels_set", NULL); diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 0b188b2b3db..897573f9fd9 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -316,7 +316,7 @@ void RNA_api_image(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); func = RNA_def_function(srna, "scale", "rna_Image_scale"); - RNA_def_function_ui_description(func, "Scale the image in pixels"); + RNA_def_function_ui_description(func, "Scale the buffer of the image, in pixels"); RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_int(func, "width", 1, 1, INT_MAX, "", "Width", 1, INT_MAX); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 456f774648a..6dd7fe53774 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -130,7 +130,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_EDGESPLIT, "Edge Split", "Split away joined faces at the edges"}, - {eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""}, + {eModifierType_Nodes, "NODES", ICON_GEOMETRY_NODES, "Geometry Nodes", ""}, {eModifierType_Mask, "MASK", ICON_MOD_MASK, @@ -6991,7 +6991,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Nodes Modifier", ""); RNA_def_struct_sdna(srna, "NodesModifierData"); RNA_def_struct_idprops_func(srna, "rna_NodesModifier_properties"); - RNA_def_struct_ui_icon(srna, ICON_NODETREE); + RNA_def_struct_ui_icon(srna, ICON_GEOMETRY_NODES); RNA_define_lib_overridable(true); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index eaf2142495e..9b9afadbd05 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -489,6 +489,13 @@ static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = { {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem rna_node_combsep_color_items[] = { + {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static const EnumPropertyItem node_sampler_type_items[] = { {0, "NEAREST", 0, "Nearest", ""}, @@ -5050,6 +5057,18 @@ static void def_fn_input_string(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_combsep_color(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_combsep_color_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -- Shader Nodes ---------------------------------------------------------- */ static void def_sh_output(StructRNA *srna) @@ -5401,6 +5420,17 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Node_update"); } +static void def_tex_combsep_color(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_combsep_color_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_image_texture(StructRNA *srna) { static const EnumPropertyItem fn_tex_prop_interpolation_items[] = { @@ -6308,6 +6338,25 @@ static void def_sh_output_aov(StructRNA *srna) RNA_def_struct_sdna_from(srna, "bNode", NULL); } +static void def_sh_combsep_color(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_sh_script(StructRNA *srna) { PropertyRNA *prop; @@ -8071,6 +8120,32 @@ static void def_cmp_ycc(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_combsep_color(StructRNA *srna) +{ + static const EnumPropertyItem mode_items[] = { + {CMP_NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {CMP_NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {CMP_NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {CMP_NODE_COMBSEP_COLOR_YCC, "YCC", ICON_NONE, "YCbCr", "Use YCbCr color processing"}, + {CMP_NODE_COMBSEP_COLOR_YUV, "YUV", ICON_NONE, "YUV", "Use YUV color processing"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCMPCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "ycc_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, node_ycc_items); + RNA_def_property_ui_text(prop, "Color Space", "Color space used for YCbCrA processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_cmp_movieclip(StructRNA *srna) { PropertyRNA *prop; @@ -12294,7 +12369,7 @@ static void rna_def_nodetree(BlenderRNA *brna) {NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"}, {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, - {NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"}, + {NTREE_GEOMETRY, "GEOMETRY", ICON_GEOMETRY_NODES, "Geometry", "Geometry nodes"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index f92ea8df459..addc8ac0c6c 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -1395,97 +1395,97 @@ static void rna_def_field(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem field_type_items[] = { - {0, "NONE", 0, "None", ""}, + {0, "NONE", ICON_BLANK1, "None", ""}, + {PFIELD_BOID, + "BOID", + ICON_FORCE_BOID, + "Boid", + "Create a force that acts as a boid's predators or target"}, + {PFIELD_CHARGE, + "CHARGE", + ICON_FORCE_CHARGE, + "Charge", + "Spherical forcefield based on the charge of particles, " + "only influences other charge force fields"}, + {PFIELD_GUIDE, + "GUIDE", + ICON_FORCE_CURVE, + "Curve Guide", + "Create a force along a curve object"}, + {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"}, + {PFIELD_FLUIDFLOW, + "FLUID_FLOW", + ICON_FORCE_FLUIDFLOW, + "Fluid Flow", + "Create a force based on fluid simulation velocities"}, {PFIELD_FORCE, "FORCE", ICON_FORCE_FORCE, "Force", "Radial field toward the center of object"}, - {PFIELD_WIND, - "WIND", - ICON_FORCE_WIND, - "Wind", - "Constant force along the force object's local Z axis"}, - {PFIELD_VORTEX, - "VORTEX", - ICON_FORCE_VORTEX, - "Vortex", - "Spiraling force that twists the force object's local Z axis"}, - {PFIELD_MAGNET, - "MAGNET", - ICON_FORCE_MAGNETIC, - "Magnetic", - "Forcefield depends on the speed of the particles"}, {PFIELD_HARMONIC, "HARMONIC", ICON_FORCE_HARMONIC, "Harmonic", "The source of this force field is the zero point of a harmonic oscillator"}, - {PFIELD_CHARGE, - "CHARGE", - ICON_FORCE_CHARGE, - "Charge", - "Spherical forcefield based on the charge of particles, " - "only influences other charge force fields"}, {PFIELD_LENNARDJ, "LENNARDJ", ICON_FORCE_LENNARDJONES, "Lennard-Jones", "Forcefield based on the Lennard-Jones potential"}, + {PFIELD_MAGNET, + "MAGNET", + ICON_FORCE_MAGNETIC, + "Magnetic", + "Forcefield depends on the speed of the particles"}, {PFIELD_TEXTURE, "TEXTURE", ICON_FORCE_TEXTURE, "Texture", "Force field based on a texture"}, - {PFIELD_GUIDE, - "GUIDE", - ICON_FORCE_CURVE, - "Curve Guide", - "Create a force along a curve object"}, - {PFIELD_BOID, - "BOID", - ICON_FORCE_BOID, - "Boid", - "Create a force that acts as a boid's predators or target"}, {PFIELD_TURBULENCE, "TURBULENCE", ICON_FORCE_TURBULENCE, "Turbulence", "Create turbulence with a noise field"}, - {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"}, - {PFIELD_FLUIDFLOW, - "FLUID_FLOW", - ICON_FORCE_FLUIDFLOW, - "Fluid Flow", - "Create a force based on fluid simulation velocities"}, + {PFIELD_VORTEX, + "VORTEX", + ICON_FORCE_VORTEX, + "Vortex", + "Spiraling force that twists the force object's local Z axis"}, + {PFIELD_WIND, + "WIND", + ICON_FORCE_WIND, + "Wind", + "Constant force along the force object's local Z axis"}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem falloff_items[] = { + {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""}, {PFIELD_FALL_SPHERE, "SPHERE", 0, "Sphere", ""}, {PFIELD_FALL_TUBE, "TUBE", 0, "Tube", ""}, - {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem texture_items[] = { - {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""}, - {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""}, {PFIELD_TEX_CURL, "CURL", 0, "Curl", ""}, + {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""}, + {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem zdirection_items[] = { - {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""}, {PFIELD_Z_POS, "POSITIVE", 0, "+Z", ""}, {PFIELD_Z_NEG, "NEGATIVE", 0, "-Z", ""}, + {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem guide_kink_items[] = { - {0, "NONE", 0, "Nothing", ""}, + {0, "NONE", 0, "None", ""}, + {4, "BRAID", 0, "Braid", ""}, {1, "CURL", 0, "Curl", ""}, {2, "RADIAL", 0, "Radial", ""}, - {3, "WAVE", 0, "Wave", ""}, - {4, "BRAID", 0, "Braid", ""}, - {5, "ROTATION", 0, "Rotation", ""}, {6, "ROLL", 0, "Roll", ""}, + {5, "ROTATION", 0, "Rotation", ""}, + {3, "WAVE", 0, "Wave", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 1a9e7f5aad8..dda467b06b8 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3292,7 +3292,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ {FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO, "category_geometry", - ICON_NODETREE, + ICON_GEOMETRY_NODES, "Geometry", "Show meshes, curves, lattice, armatures and metaballs data"}, {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, @@ -5407,6 +5407,17 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Show Mask Editor", "Show Mask editing related properties"); + /* Gizmo Toggles. */ + prop = RNA_def_property(srna, "show_gizmo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE); + RNA_def_property_ui_text(prop, "Show Gizmo", "Show gizmos of all types"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + + prop = RNA_def_property(srna, "show_gizmo_navigate", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE_NAVIGATE); + RNA_def_property_ui_text(prop, "Navigate Gizmo", "Viewport navigation gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + /* Overlays */ prop = RNA_def_property(srna, "overlay", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 5ac324b3627..b3d4ae80713 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6424,6 +6424,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1); RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode"); + prop = RNA_def_property(srna, "use_draw_manager_acquire_lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_draw_manager_acquire_lock", 1); + RNA_def_property_ui_text( + prop, "Draw Manager Locking", "Don't lock UI during background rendering"); + prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text(prop, "Extended Asset Browser", diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b09a9ab0733..0ac6dc7aaa9 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -896,8 +896,10 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value) } } -/* assumes value to be an enum from rna_enum_event_type_items */ -/* function makes sure keymodifiers are only valid keys, ESC keeps it unaltered */ +/** + * Assumes value to be an enum from rna_enum_event_type_items. + * Function makes sure keymodifiers are only valid keys, ESC keeps it unaltered. + */ static void rna_wmKeyMapItem_keymodifier_set(PointerRNA *ptr, int value) { wmKeyMapItem *kmi = ptr->data; diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index a04b29b8815..696d2d0f31d 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -1196,6 +1196,50 @@ static int rna_XrEventData_action_length(PointerRNA *ptr) # endif } +static void rna_XrEventData_user_path_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_user_path_other_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path_other); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_other_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path_other); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrEventData_type_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -2402,6 +2446,19 @@ static void rna_def_xr_eventdata(BlenderRNA *brna) prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL); RNA_def_property_ui_text(prop, "Action", "XR action name"); + prop = RNA_def_property(srna, "user_path", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_get", "rna_XrEventData_user_path_length", NULL); + RNA_def_property_ui_text(prop, "User Path", "User path of the action. E.g. \"/user/hand/left\""); + + prop = RNA_def_property(srna, "user_path_other", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_other_get", "rna_XrEventData_user_path_other_length", NULL); + RNA_def_property_ui_text( + prop, "User Path Other", "Other user path, for bimanual actions. E.g. \"/user/hand/right\""); + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_items(prop, rna_enum_xr_action_types); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a5e5bf36dcd..1aac3c2191d 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -70,7 +70,7 @@ set(SRC intern/MOD_normal_edit.c intern/MOD_ocean.c intern/MOD_particleinstance.c - intern/MOD_particlesystem.c + intern/MOD_particlesystem.cc intern/MOD_remesh.c intern/MOD_screw.c intern/MOD_shapekey.c diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 21041e8e1b2..cdf16d813f3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -985,17 +985,16 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store( if (!component.attribute_domain_supported(domain)) { continue; } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); blender::bke::GeometryComponentFieldContext field_context{component, domain}; - blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_num}; for (const OutputAttributeInfo &output_info : outputs_info) { const CPPType &type = output_info.field.cpp_type(); OutputAttributeToStore store{ component_type, domain, output_info.name, - GMutableSpan{ - type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}}; + GMutableSpan{type, MEM_malloc_arrayN(domain_num, type.size(), __func__), domain_num}}; field_evaluator.add_with_destination(output_info.field, store.data); attributes_to_store.append(store); } @@ -1799,7 +1798,7 @@ ModifierTypeInfo modifierType_Nodes = { eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), - /* icon */ ICON_NODETREE, + /* icon */ ICON_GEOMETRY_NODES, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.cc index 032227307e7..0f75038189a 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -51,11 +51,11 @@ static void freeData(ModifierData *md) ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } psmd->totdmvert = psmd->totdmedge = psmd->totdmface = 0; @@ -81,8 +81,8 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla * code has to be called then to ensure proper remapping of that pointer. See e.g. * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */ - tpsmd->mesh_final = NULL; - tpsmd->mesh_original = NULL; + tpsmd->mesh_final = nullptr; + tpsmd->mesh_original = nullptr; tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0; } @@ -104,7 +104,7 @@ static void deformVerts(ModifierData *md, { Mesh *mesh_src = mesh; ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - ParticleSystem *psys = NULL; + ParticleSystem *psys = nullptr; if (ctx->object->particlesystem.first) { psys = psmd->psys; @@ -117,28 +117,28 @@ static void deformVerts(ModifierData *md, return; } - if (mesh_src == NULL) { + if (mesh_src == nullptr) { mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, NULL, vertexCos, verts_num, false, true); - if (mesh_src == NULL) { + ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true); + if (mesh_src == nullptr) { return; } } /* clear old dm */ - bool had_mesh_final = (psmd->mesh_final != NULL); + bool had_mesh_final = (psmd->mesh_final != nullptr); if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } else if (psmd->flag & eParticleSystemFlag_file_loaded) { /* in file read mesh just wasn't saved in file so no need to reset everything */ psmd->flag &= ~eParticleSystemFlag_file_loaded; - if (psys->particles == NULL) { + if (psys->particles == nullptr) { psys->recalc |= ID_RECALC_PSYS_RESET; } /* TODO(sergey): This is not how particles were working prior to copy on @@ -165,18 +165,18 @@ static void deformVerts(ModifierData *md, /* Get the original mesh from the object, this is what the particles * are attached to so in case of non-deform modifiers we need to remap * them to the final mesh (typically subdivision surfaces). */ - Mesh *mesh_original = NULL; + Mesh *mesh_original = nullptr; if (ctx->object->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(ctx->object); if (em) { /* In edit mode get directly from the edit mesh. */ - psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, mesh); + psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, mesh); } else { /* Otherwise get regular mesh. */ - mesh_original = ctx->object->data; + mesh_original = static_cast<Mesh *>(ctx->object->data); } } else { @@ -193,8 +193,8 @@ static void deformVerts(ModifierData *md, BKE_mesh_tessface_ensure(psmd->mesh_original); } - if (!ELEM(mesh_src, NULL, mesh, psmd->mesh_final)) { - BKE_id_free(NULL, mesh_src); + if (!ELEM(mesh_src, nullptr, mesh, psmd->mesh_final)) { + BKE_id_free(nullptr, mesh_src); } /* Report change in mesh structure. @@ -221,7 +221,7 @@ static void deformVerts(ModifierData *md, if (DEG_is_active(ctx->depsgraph)) { Object *object_orig = DEG_get_original_object(ctx->object); ModifierData *md_orig = BKE_modifiers_findby_name(object_orig, psmd->modifier.name); - BLI_assert(md_orig != NULL); + BLI_assert(md_orig != nullptr); ParticleSystemModifierData *psmd_orig = (ParticleSystemModifierData *)md_orig; psmd_orig->flag = psmd->flag; } @@ -237,16 +237,16 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - const bool do_temp_mesh = (mesh == NULL); + const bool do_temp_mesh = (mesh == nullptr); if (do_temp_mesh) { mesh = BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name); - BM_mesh_bm_to_me(NULL, editData->bm, mesh, &((BMeshToMeshParams){0})); + BM_mesh_bm_to_me(nullptr, editData->bm, mesh, &((BMeshToMeshParams){0})); } deformVerts(md, ob, mesh, vertexCos, verts_num); if (derivedData) { - BKE_id_free(NULL, mesh); + BKE_id_free(nullptr, mesh); } } #endif @@ -258,7 +258,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA ob_ptr; PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - Object *ob = ob_ptr.data; + Object *ob = static_cast<Object *>(ob_ptr.data); ModifierData *md = (ModifierData *)ptr->data; ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; @@ -291,8 +291,8 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) { ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - psmd->mesh_final = NULL; - psmd->mesh_original = NULL; + psmd->mesh_final = nullptr; + psmd->mesh_original = nullptr; /* This is written as part of ob->particlesystem. */ BLO_read_data_address(reader, &psmd->psys); psmd->flag &= ~eParticleSystemFlag_psys_updated; @@ -315,23 +315,23 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* copyData */ copyData, /* deformVerts */ deformVerts, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ NULL, - /* modifyGeometrySet */ NULL, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ nullptr, + /* modifyGeometrySet */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, + /* blendWrite */ nullptr, /* blendRead */ blendRead, }; diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index aef254b1103..c37d6a3f2c1 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -11,6 +11,10 @@ #include "DEG_depsgraph_build.h" +#ifdef __cplusplus +extern "C" { +#endif + struct MDeformVert; struct Mesh; struct ModifierData; @@ -51,3 +55,7 @@ void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, struct Object *object, const char *bonename, const char *description); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 3d3450d9252..5d782674f16 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -126,6 +126,8 @@ void register_node_type_cmp_planetrackdeform(void); void register_node_type_cmp_cornerpin(void); void register_node_type_cmp_separate_xyz(void); void register_node_type_cmp_combine_xyz(void); +void register_node_type_cmp_separate_color(void); +void register_node_type_cmp_combine_color(void); void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node); void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index cde4b67e120..ad96fba1929 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -8,6 +8,7 @@ extern "C" { void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); +void register_node_type_fn_combine_color(void); void register_node_type_fn_compare(void); void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_bool(void); @@ -19,6 +20,7 @@ void register_node_type_fn_input_vector(void); void register_node_type_fn_random_value(void); void register_node_type_fn_replace_string(void); void register_node_type_fn_rotate_euler(void); +void register_node_type_fn_separate_color(void); void register_node_type_fn_slice_string(void); void register_node_type_fn_string_length(void); void register_node_type_fn_value_to_string(void); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 1ad859aa47b..4fbf5192222 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -103,16 +103,16 @@ class GeometryValueLog : public ValueLog { public: struct MeshInfo { - int tot_verts, tot_edges, tot_faces; + int verts_num, edges_num, faces_num; }; struct CurveInfo { - int tot_splines; + int splines_num; }; struct PointCloudInfo { - int tot_points; + int points_num; }; struct InstancesInfo { - int tot_instances; + int instances_num; }; std::optional<MeshInfo> mesh_info; diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 4996f12e27d..1d1310360b8 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -43,6 +43,8 @@ void register_node_type_sh_vect_math(void); void register_node_type_sh_squeeze(void); void register_node_type_sh_dynamic(void); void register_node_type_sh_invert(void); +void register_node_type_sh_sepcolor(void); +void register_node_type_sh_combcolor(void); void register_node_type_sh_seprgb(void); void register_node_type_sh_combrgb(void); void register_node_type_sh_sephsv(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5818fa631e7..7dd732e7fad 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -42,8 +42,8 @@ DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" ) DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" ) DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" ) -DefNode(ShaderNode, SH_NODE_SEPRGB, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" ) -DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRGB", CombineRGB, "Combine RGB", "" ) +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_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" ) @@ -106,8 +106,8 @@ DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" ) DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "" ) DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" ) -DefNode(ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) -DefNode(ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) +DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) +DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" ) DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "" ) DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" ) @@ -120,6 +120,8 @@ DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" ) DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" ) +DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) +DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) @@ -137,8 +139,8 @@ DefNode(CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VA DefNode(CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" ) DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time Curve", "" ) DefNode(CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" ) -DefNode(CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) -DefNode(CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPRGBA_LEGACY, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPHSVA_LEGACY, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) DefNode(CompositorNode, CMP_NODE_SETALPHA, def_cmp_set_alpha, "SETALPHA", SetAlpha, "Set Alpha", "" ) DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" ) DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" ) @@ -149,16 +151,16 @@ DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" ) DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" ) DefNode(CompositorNode, CMP_NODE_ZCOMBINE, def_cmp_zcombine, "ZCOMBINE", Zcombine, "Z Combine", "" ) -DefNode(CompositorNode, CMP_NODE_COMBRGBA, 0, "COMBRGBA", CombRGBA, "Combine RGBA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBRGBA_LEGACY,0, "COMBRGBA", CombRGBA, "Combine RGBA", "" ) DefNode(CompositorNode, CMP_NODE_DILATEERODE, def_cmp_dilate_erode, "DILATEERODE", DilateErode, "Dilate/Erode", "" ) DefNode(CompositorNode, CMP_NODE_INPAINT, def_cmp_inpaint, "INPAINT", Inpaint, "Inpaint", "" ) DefNode(CompositorNode, CMP_NODE_DESPECKLE, def_cmp_despeckle, "DESPECKLE", Despeckle, "Despeckle", "" ) DefNode(CompositorNode, CMP_NODE_ROTATE, def_cmp_rotate, "ROTATE", Rotate, "Rotate", "" ) DefNode(CompositorNode, CMP_NODE_SCALE, def_cmp_scale, "SCALE", Scale, "Scale", "" ) -DefNode(CompositorNode, CMP_NODE_SEPYCCA, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" ) -DefNode(CompositorNode, CMP_NODE_COMBYCCA, def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" ) -DefNode(CompositorNode, CMP_NODE_SEPYUVA, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" ) -DefNode(CompositorNode, CMP_NODE_COMBYUVA, 0, "COMBYUVA", CombYUVA, "Combine YUVA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPYCCA_LEGACY, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBYCCA_LEGACY,def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPYUVA_LEGACY, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBYUVA_LEGACY,0, "COMBYUVA", CombYUVA, "Combine YUVA", "" ) DefNode(CompositorNode, CMP_NODE_DIFF_MATTE, def_cmp_diff_matte, "DIFF_MATTE", DiffMatte, "Difference Key", "" ) DefNode(CompositorNode, CMP_NODE_COLOR_SPILL, def_cmp_color_spill, "COLOR_SPILL", ColorSpill, "Color Spill", "" ) DefNode(CompositorNode, CMP_NODE_CHROMA_MATTE, def_cmp_chroma_matte, "CHROMA_MATTE", ChromaMatte, "Chroma Key", "" ) @@ -170,7 +172,7 @@ DefNode(CompositorNode, CMP_NODE_ID_MASK, def_cmp_id_mask, "ID_MAS DefNode(CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask", "" ) DefNode(CompositorNode, CMP_NODE_DEFOCUS, def_cmp_defocus, "DEFOCUS", Defocus, "Defocus", "" ) DefNode(CompositorNode, CMP_NODE_DISPLACE, 0, "DISPLACE", Displace, "Displace", "" ) -DefNode(CompositorNode, CMP_NODE_COMBHSVA, 0, "COMBHSVA", CombHSVA, "Combine HSVA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBHSVA_LEGACY,0, "COMBHSVA", CombHSVA, "Combine HSVA", "" ) DefNode(CompositorNode, CMP_NODE_MATH, def_math, "MATH", Math, "Math", "" ) DefNode(CompositorNode, CMP_NODE_LUMA_MATTE, def_cmp_luma_matte, "LUMA_MATTE", LumaMatte, "Luminance Key", "" ) DefNode(CompositorNode, CMP_NODE_BRIGHTCONTRAST, def_cmp_brightcontrast, "BRIGHTCONTRAST", BrightContrast, "Bright/Contrast", "" ) @@ -215,9 +217,11 @@ DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSU 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_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) +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", "" ) +DefNode(CompositorNode, CMP_NODE_SEPARATE_COLOR, def_cmp_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) +DefNode(CompositorNode, CMP_NODE_COMBINE_COLOR, def_cmp_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) @@ -237,11 +241,13 @@ DefNode(TextureNode, TEX_NODE_VIEWER, 0, "VIEWER DefNode(TextureNode, TEX_NODE_TRANSLATE, 0, "TRANSLATE", Translate, "Translate", "" ) DefNode(TextureNode, TEX_NODE_COORD, 0, "COORD", Coordinates, "Coordinates", "" ) DefNode(TextureNode, TEX_NODE_DISTANCE, 0, "DISTANCE", Distance, "Distance", "" ) -DefNode(TextureNode, TEX_NODE_COMPOSE, 0, "COMPOSE", Compose, "Combine RGBA", "" ) -DefNode(TextureNode, TEX_NODE_DECOMPOSE, 0, "DECOMPOSE", Decompose, "Separate RGBA", "" ) +DefNode(TextureNode, TEX_NODE_COMPOSE_LEGACY, 0, "COMPOSE", Compose, "Combine RGBA", "" ) +DefNode(TextureNode, TEX_NODE_DECOMPOSE_LEGACY,0, "DECOMPOSE", Decompose, "Separate RGBA", "" ) DefNode(TextureNode, TEX_NODE_VALTONOR, 0, "VALTONOR", ValToNor, "Value to Normal", "" ) DefNode(TextureNode, TEX_NODE_SCALE, 0, "SCALE", Scale, "Scale", "" ) DefNode(TextureNode, TEX_NODE_AT, 0, "AT", At, "At", "" ) +DefNode(TextureNode, TEX_NODE_COMBINE_COLOR, def_tex_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) +DefNode(TextureNode, TEX_NODE_SEPARATE_COLOR, def_tex_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) /* procedural textures */ DefNode(TextureNode, TEX_NODE_PROC+TEX_VORONOI, 0, "TEX_VORONOI", TexVoronoi, "Voronoi", "" ) DefNode(TextureNode, TEX_NODE_PROC+TEX_BLEND, 0, "TEX_BLEND", TexBlend, "Blend", "" ) @@ -256,6 +262,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") +DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "") DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "") @@ -267,6 +274,7 @@ DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") +DefNode(FunctionNode, FN_NODE_SEPARATE_COLOR, def_fn_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "") DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h index 0f07b17f165..9a2dc705c0d 100644 --- a/source/blender/nodes/NOD_texture.h +++ b/source/blender/nodes/NOD_texture.h @@ -46,6 +46,8 @@ void register_node_type_tex_at(void); void register_node_type_tex_compose(void); void register_node_type_tex_decompose(void); +void register_node_type_tex_combine_color(void); +void register_node_type_tex_separate_color(void); void register_node_type_tex_proc_voronoi(void); void register_node_type_tex_proc_blend(void); diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 57f76f20ac6..c0100d77889 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC nodes/node_composite_rotate.cc nodes/node_composite_scale.cc nodes/node_composite_scene_time.cc + nodes/node_composite_sepcomb_color.cc nodes/node_composite_sepcomb_hsva.cc nodes/node_composite_sepcomb_rgba.cc nodes/node_composite_sepcomb_xyz.cc diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc new file mode 100644 index 00000000000..b253656a628 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_composite_util.hh" + +static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCMPCombSepColor *data = MEM_cnew<NodeCMPCombSepColor>(__func__); + data->mode = CMP_NODE_COMBSEP_COLOR_RGB; + data->ycc_mode = BLI_YCC_ITU_BT709; + node->storage = data; +} + +static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSepColorMode mode) +{ + bNodeSocket *sock1 = (bNodeSocket *)sockets->first; + bNodeSocket *sock2 = sock1->next; + bNodeSocket *sock3 = sock2->next; + + node_sock_label_clear(sock1); + node_sock_label_clear(sock2); + node_sock_label_clear(sock3); + + switch (mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + node_sock_label(sock1, "Red"); + node_sock_label(sock2, "Green"); + node_sock_label(sock3, "Blue"); + break; + case CMP_NODE_COMBSEP_COLOR_HSV: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Value"); + break; + case CMP_NODE_COMBSEP_COLOR_HSL: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Lightness"); + break; + case CMP_NODE_COMBSEP_COLOR_YCC: + node_sock_label(sock1, "Y"); + node_sock_label(sock2, "Cb"); + node_sock_label(sock3, "Cr"); + break; + case CMP_NODE_COMBSEP_COLOR_YUV: + node_sock_label(sock1, "Y"); + node_sock_label(sock2, "U"); + node_sock_label(sock3, "V"); + break; + default: + BLI_assert_unreachable(); + break; + } +} + +/* **************** SEPARATE COLOR ******************** */ + +namespace blender::nodes::node_composite_separate_color_cc { + +static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); + b.add_output<decl::Float>(N_("Alpha")); +} + +static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode); +} + +} // namespace blender::nodes::node_composite_separate_color_cc + +void register_node_type_cmp_separate_color() +{ + namespace file_ns = blender::nodes::node_composite_separate_color_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_separate_color_declare; + node_type_init(&ntype, node_cmp_combsep_color_init); + node_type_storage( + &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::cmp_node_separate_color_update); + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE COLOR ******************** */ + +namespace blender::nodes::node_composite_combine_color_cc { + +static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Image")); +} + +static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode); +} + +} // namespace blender::nodes::node_composite_combine_color_cc + +void register_node_type_cmp_combine_color() +{ + namespace file_ns = blender::nodes::node_composite_combine_color_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combine_color_declare; + node_type_init(&ntype, node_cmp_combsep_color_init); + node_type_storage( + &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::cmp_node_combine_color_update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index 349c27d876d..a0d2485ea5a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -28,7 +28,7 @@ void register_node_type_cmp_sephsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; nodeRegisterType(&ntype); } @@ -54,7 +54,7 @@ void register_node_type_cmp_combhsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index c46603be847..ae46681b0f4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -27,7 +27,7 @@ void register_node_type_cmp_seprgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; nodeRegisterType(&ntype); @@ -54,7 +54,7 @@ void register_node_type_cmp_combrgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 9b5c153fddf..a3c40b61e64 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -33,7 +33,7 @@ void register_node_type_cmp_sepycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); @@ -66,7 +66,7 @@ void register_node_type_cmp_combycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index e458c9cfb7e..7fdece5904d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -28,7 +28,7 @@ void register_node_type_cmp_sepyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; nodeRegisterType(&ntype); @@ -55,7 +55,7 @@ void register_node_type_cmp_combyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt index 6ccc4c7bf5c..d03f1cb63ff 100644 --- a/source/blender/nodes/function/CMakeLists.txt +++ b/source/blender/nodes/function/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC set(SRC nodes/node_fn_align_euler_to_vector.cc nodes/node_fn_boolean_math.cc + nodes/node_fn_combine_color.cc nodes/node_fn_compare.cc nodes/node_fn_float_to_int.cc nodes/node_fn_input_bool.cc @@ -31,6 +32,7 @@ set(SRC nodes/node_fn_random_value.cc nodes/node_fn_replace_string.cc nodes/node_fn_rotate_euler.cc + nodes/node_fn_separate_color.cc nodes/node_fn_slice_string.cc nodes/node_fn_string_length.cc nodes/node_fn_value_to_string.cc diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc new file mode 100644 index 00000000000..c5fd3ce38a1 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void fn_node_combine_color_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); +}; + +static void fn_node_combine_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void fn_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode); +} + +static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +static const fn::MultiFunction *get_multi_function(bNode &bnode) +{ + const NodeCombSepColor &storage = node_storage(bnode); + + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> rgba_fn{ + "RGB", [](float r, float g, float b, float a) { return ColorGeometry4f(r, g, b, a); }}; + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsva_fn{ + "HSV", [](float h, float s, float v, float a) { + ColorGeometry4f r_color; + hsv_to_rgb(h, s, v, &r_color.r, &r_color.g, &r_color.b); + r_color.a = a; + return r_color; + }}; + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsla_fn{ + "HSL", [](float h, float s, float l, float a) { + ColorGeometry4f color; + hsl_to_rgb(h, s, l, &color.r, &color.g, &color.b); + color.a = a; + return color; + }}; + + switch (storage.mode) { + case NODE_COMBSEP_COLOR_RGB: + return &rgba_fn; + case NODE_COMBSEP_COLOR_HSV: + return &hsva_fn; + case NODE_COMBSEP_COLOR_HSL: + return &hsla_fn; + } + + BLI_assert_unreachable(); + return nullptr; +} + +static void fn_node_combine_color_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_combine_color(void) +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::fn_node_combine_color_declare; + node_type_update(&ntype, blender::nodes::fn_node_combine_color_update); + node_type_init(&ntype, blender::nodes::fn_node_combine_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_combine_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_combine_color_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_separate_color.cc b/source/blender/nodes/function/nodes/node_fn_separate_color.cc new file mode 100644 index 00000000000..1701dfdc6fa --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_separate_color.cc @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void fn_node_separate_color_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); + b.add_output<decl::Float>(N_("Alpha")); +}; + +static void fn_node_separate_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void fn_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode); +} + +static void fn_node_separate_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +class SeparateRGBAFunction : public fn::MultiFunction { + public: + SeparateRGBAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Red"); + signature.single_output<float>("Green"); + signature.single_output<float>("Blue"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> red = params.uninitialized_single_output<float>(1, "Red"); + MutableSpan<float> green = params.uninitialized_single_output<float>(2, "Green"); + MutableSpan<float> blue = params.uninitialized_single_output<float>(3, "Blue"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + red[i] = colors[i].r; + green[i] = colors[i].g; + blue[i] = colors[i].b; + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +class SeparateHSVAFunction : public fn::MultiFunction { + public: + SeparateHSVAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Hue"); + signature.single_output<float>("Saturation"); + signature.single_output<float>("Value"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue"); + MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation"); + MutableSpan<float> value = params.uninitialized_single_output<float>(3, "Value"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + rgb_to_hsv(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &value[i]); + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +class SeparateHSLAFunction : public fn::MultiFunction { + public: + SeparateHSLAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Hue"); + signature.single_output<float>("Saturation"); + signature.single_output<float>("Lightness"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue"); + MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation"); + MutableSpan<float> lightness = params.uninitialized_single_output<float>(3, "Lightness"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + rgb_to_hsl(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &lightness[i]); + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +static void fn_node_separate_color_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const NodeCombSepColor &storage = node_storage(builder.node()); + + switch (storage.mode) { + case NODE_COMBSEP_COLOR_RGB: { + static SeparateRGBAFunction fn; + builder.set_matching_fn(fn); + break; + } + case NODE_COMBSEP_COLOR_HSV: { + static SeparateHSVAFunction fn; + builder.set_matching_fn(fn); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + static SeparateHSLAFunction fn; + builder.set_matching_fn(fn); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +} // namespace blender::nodes + +void register_node_type_fn_separate_color(void) +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::fn_node_separate_color_declare; + node_type_update(&ntype, blender::nodes::fn_node_separate_color_update); + node_type_init(&ntype, blender::nodes::fn_node_separate_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_separate_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_separate_color_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index e081e007c81..38e914b9a9f 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -110,7 +110,7 @@ void register_node_tree_type_geo() tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = ICON_NODETREE; + tt->ui_icon = ICON_GEOMETRY_NODES; strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 7af3159bbf8..8f20da66c3b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -57,8 +57,6 @@ Mesh *create_cylinder_or_cone_mesh(float radius_top, GeometryNodeMeshCircleFillType fill_type, ConeAttributeOutputs &attribute_outputs); -Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); - /** * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. */ @@ -81,14 +79,4 @@ void separate_geometry(GeometrySet &geometry_set, std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); -class SplineLengthFieldInput final : public GeometryFieldInput { - public: - SplineLengthFieldInput(); - GVArray get_varray_for_context(const GeometryComponent &component, - AttributeDomain domain, - IndexMask mask) const final; - uint64_t hash() const override; - bool is_equal_to(const fn::FieldNode &other) const override; -}; - } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index ea26eec0c15..b29831ceeb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -217,16 +217,16 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); const VArray<T> &values = evaluator.get_evaluated<T>(0); const VArray<int> &group_indices = evaluator.get_evaluated<int>(1); - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); if (group_indices.is_single()) { T accumulation = T(); @@ -303,9 +303,9 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -317,10 +317,10 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { for (const int i : values.index_range()) { accumulation = values[i] + accumulation; } - return VArray<T>::ForSingle(accumulation, domain_size); + return VArray<T>::ForSingle(accumulation, domain_num); } - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); Map<int, T> accumulations; for (const int i : values.index_range()) { T &value = accumulations.lookup_or_add_default(group_indices[i]); 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 45a6aabeb03..18cf005c965 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -112,8 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); OutputAttribute output_attribute = component.attribute_try_get_for_output_only( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index b3fe9d160b3..8ab0eb678e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -72,11 +72,11 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_MESH: { if (geometry_set.has_mesh()) { const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE)); - params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE)); + params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE)); params.set_output("Face Corner Count", - component->attribute_domain_size(ATTR_DOMAIN_CORNER)); + component->attribute_domain_num(ATTR_DOMAIN_CORNER)); } else { params.set_default_remaining_outputs(); @@ -86,8 +86,8 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_CURVE: { if (geometry_set.has_curves()) { const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE)); } else { params.set_default_remaining_outputs(); @@ -98,7 +98,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_pointcloud()) { const PointCloudComponent *component = geometry_set.get_component_for_read<PointCloudComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); } else { params.set_default_remaining_outputs(); @@ -109,8 +109,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>(); - params.set_output("Instance Count", - component->attribute_domain_size(ATTR_DOMAIN_INSTANCE)); + params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1153f18ffd4..c7f65a68d60 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -197,9 +197,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); @@ -275,9 +275,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 558129fb384..00b10cc8a2f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "GEO_mesh_primitive_cuboid.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_bounding_box_cc { @@ -53,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) else { const float3 scale = sub_max - sub_min; const float3 center = sub_min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map"); transform_mesh(*mesh, center, float3(0), float3(1)); sub_geometry.replace_mesh(mesh); sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 09d0f13c50d..31f706c497c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -138,14 +138,14 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) { int span_count = 0; int count = 0; - int total_size = 0; + int total_num = 0; Span<float3> positions_span; if (geometry_set.has_mesh()) { count++; const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT); + total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT); } if (geometry_set.has_pointcloud()) { @@ -155,7 +155,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) geometry_set.get_component_for_read<PointCloudComponent>(); VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - total_size += varray.size(); + total_num += varray.size(); positions_span = varray.get_internal_span(); } @@ -165,7 +165,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); positions_span = curves.evaluated_positions(); - total_size += positions_span.size(); + total_num += positions_span.size(); } if (count == 0) { @@ -178,7 +178,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) return hull_from_bullet(nullptr, positions_span); } - Array<float3> positions(total_size); + Array<float3> positions(total_num); int offset = 0; if (geometry_set.has_mesh()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 95ea978541c..fb8a488ddae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -106,13 +106,13 @@ static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const /* Calculate the direction vectors from each vertex to their previous vertex. */ static Array<float3> calculate_directions(const Span<float3> positions) { - const int size = positions.size(); - Array<float3> directions(size); + const int num = positions.size(); + Array<float3> directions(num); - for (const int i : IndexRange(size - 1)) { + for (const int i : IndexRange(num - 1)) { directions[i] = math::normalize(positions[i + 1] - positions[i]); } - directions[size - 1] = math::normalize(positions[0] - positions[size - 1]); + directions[num - 1] = math::normalize(positions[0] - positions[num - 1]); return directions; } @@ -120,11 +120,11 @@ static Array<float3> calculate_directions(const Span<float3> positions) /* Calculate the axes around which the fillet is built. */ static Array<float3> calculate_axes(const Span<float3> directions) { - const int size = directions.size(); - Array<float3> axes(size); + const int num = directions.size(); + Array<float3> axes(num); - axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0])); - for (const int i : IndexRange(1, size - 1)) { + axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0])); + for (const int i : IndexRange(1, num - 1)) { axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); } @@ -134,11 +134,11 @@ static Array<float3> calculate_axes(const Span<float3> directions) /* Calculate the angle of the arc formed by the fillet. */ static Array<float> calculate_angles(const Span<float3> directions) { - const int size = directions.size(); - Array<float> angles(size); + const int num = directions.size(); + Array<float> angles(num); - angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]); - for (const int i : IndexRange(1, size - 1)) { + angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]); + for (const int i : IndexRange(1, num - 1)) { angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); } @@ -147,18 +147,18 @@ static Array<float> calculate_angles(const Span<float3> directions) /* Calculate the segment count in each filleted arc. */ static Array<int> calculate_counts(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset, const bool cyclic) { - Array<int> counts(size, 1); + Array<int> counts(num, 1); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { counts[i] = fillet_param.counts[spline_offset + i]; } } if (!cyclic) { - counts[0] = counts[size - 1] = 0; + counts[0] = counts[num - 1] = 0; } return counts; @@ -166,17 +166,17 @@ static Array<int> calculate_counts(const FilletParam &fillet_param, /* Calculate the radii for the vertices to be filleted. */ static Array<float> calculate_radii(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset) { - Array<float> radii(size, 0.0f); + Array<float> radii(num, 0.0f); if (fillet_param.limit_radius) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); } } else { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = fillet_param.radii[spline_offset + i]; } } @@ -207,15 +207,15 @@ static FilletData calculate_fillet_data(const Spline &spline, MutableSpan<int> point_counts, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); FilletData fd; fd.directions = calculate_directions(spline.positions()); fd.positions = spline.positions(); fd.axes = calculate_axes(fd.directions); fd.angles = calculate_angles(fd.directions); - fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic()); - fd.radii = calculate_radii(fillet_param, size, spline_offset); + fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic()); + fd.radii = calculate_radii(fillet_param, num, spline_offset); added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); @@ -229,19 +229,19 @@ static void limit_radii(FilletData &fd, const bool cyclic) Span<float> angles(fd.angles); Span<float3> positions(fd.positions); - const int size = radii.size(); - const int fillet_count = cyclic ? size : size - 2; + const int num = radii.size(); + const int fillet_count = cyclic ? num : num - 2; const int start = cyclic ? 0 : 1; - Array<float> max_radii(size, FLT_MAX); + Array<float> max_radii(num, FLT_MAX); if (cyclic) { /* Calculate lengths between adjacent control points. */ - const float len_prev = math::distance(positions[0], positions[size - 1]); + const float len_prev = math::distance(positions[0], positions[num - 1]); const float len_next = math::distance(positions[0], positions[1]); /* Calculate tangent lengths of fillets in control points. */ const float tan_len = radii[0] * tan(angles[0] / 2.0f); - const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f); + const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f); const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); float factor_prev = 1.0f, factor_next = 1.0f; @@ -255,12 +255,12 @@ static void limit_radii(FilletData &fd, const bool cyclic) /* Scale max radii by calculated factors. */ max_radii[0] = radii[0] * std::min(factor_next, factor_prev); max_radii[1] = radii[1] * factor_next; - max_radii[size - 1] = radii[size - 1] * factor_prev; + max_radii[num - 1] = radii[num - 1] * factor_prev; } /* Initialize max_radii to largest possible radii. */ float prev_dist = math::distance(positions[1], positions[0]); - for (const int i : IndexRange(1, size - 2)) { + for (const int i : IndexRange(1, num - 2)) { const float temp_dist = math::distance(positions[i], positions[i + 1]); max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); prev_dist = temp_dist; @@ -282,7 +282,7 @@ static void limit_radii(FilletData &fd, const bool cyclic) } /* Assign the max_radii to the fillet data's radii. */ - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::min(radii[i], max_radii[i]); } } @@ -358,10 +358,10 @@ static void update_bezier_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -385,7 +385,7 @@ static void update_bezier_positions(const FilletData &fd, /* Position the end points of the arc and their handles. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -442,10 +442,10 @@ static void update_poly_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -460,7 +460,7 @@ static void update_poly_positions(const FilletData &fd, /* Position the end points of the arc. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -487,15 +487,15 @@ static SplinePtr fillet_spline(const Spline &spline, const FilletParam &fillet_param, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); const bool cyclic = spline.is_cyclic(); - if (size < 3) { + if (num < 3) { return spline.copy(); } /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ - Array<int> point_counts(size, 1); + Array<int> point_counts(num, 1); int added_count = 0; /* Update point_counts array and added_count. */ @@ -505,7 +505,7 @@ static SplinePtr fillet_spline(const Spline &spline, limit_radii(fd, cyclic); } - const int total_points = added_count + size; + const int total_points = added_count + num; const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points); SplinePtr dst_spline_ptr = spline.copy_only_settings(); (*dst_spline_ptr).resize(total_points); @@ -581,8 +581,8 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add(radius_field); 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 e0348f27e51..d9cc8bcf023 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -1,12 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_array.hh" -#include "BLI_index_mask_ops.hh" -#include "BLI_length_parameterize.hh" -#include "BLI_task.hh" -#include "BLI_timeit.hh" +#include "GEO_resample_curves.hh" -#include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "UI_interface.h" @@ -56,556 +51,6 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -/** Returns the number of evaluated points in each curve. Used to deselect curves with none. */ -class EvaluatedCountFieldInput final : public GeometryFieldInput { - public: - EvaluatedCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Evaluated Point Count") - { - category_ = Category::Generated; - } - - GVArray get_varray_for_context(const GeometryComponent &component, - const AttributeDomain domain, - IndexMask UNUSED(mask)) const final - { - if (component.type() == GEO_COMPONENT_TYPE_CURVE && domain == ATTR_DOMAIN_CURVE && - !component.is_empty()) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - curves.ensure_evaluated_offsets(); - return VArray<int>::ForFunc(curves.curves_num(), [&](const int64_t index) -> int { - return curves.evaluated_points_for_curve(index).size(); - }); - } - return {}; - } - - uint64_t hash() const override - { - /* Some random constant hash. */ - return 234905872379865; - } - - bool is_equal_to(const fn::FieldNode &other) const override - { - return dynamic_cast<const EvaluatedCountFieldInput *>(&other) != nullptr; - } -}; - -/** - * Return true if the attribute should be copied/interpolated to the result curves. - * Don't output attributes that correspond to curve types that have no curves in the result. - */ -static bool interpolate_attribute_to_curves(const AttributeIDRef &attribute_id, - const std::array<int, CURVE_TYPES_NUM> &type_counts) -{ - if (!attribute_id.is_named()) { - return true; - } - if (ELEM(attribute_id.name(), - "handle_type_left", - "handle_type_right", - "handle_left", - "handle_right")) { - return type_counts[CURVE_TYPE_BEZIER] != 0; - } - if (ELEM(attribute_id.name(), "nurbs_weight")) { - return type_counts[CURVE_TYPE_NURBS] != 0; - } - return true; -} - -/** - * Return true if the attribute should be copied to poly curves. - */ -static bool interpolate_attribute_to_poly_curve(const AttributeIDRef &attribute_id) -{ - static const Set<StringRef> no_interpolation{{ - "handle_type_left", - "handle_type_right", - "handle_position_right", - "handle_position_left", - "nurbs_weight", - }}; - return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); -} - -/** - * Retrieve spans from source and result attributes. - */ -static void retrieve_attribute_spans(const Span<AttributeIDRef> ids, - const CurveComponent &src_component, - CurveComponent &dst_component, - Vector<GSpan> &src, - Vector<GMutableSpan> &dst, - Vector<OutputAttribute> &dst_attributes) -{ - for (const int i : ids.index_range()) { - GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); - BLI_assert(src_attribute); - src.append(src_attribute.get_internal_span()); - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - ids[i], ATTR_DOMAIN_POINT, data_type); - dst.append(dst_attribute.as_span()); - dst_attributes.append(std::move(dst_attribute)); - } -} - -struct AttributesForInterpolation : NonCopyable, NonMovable { - Vector<GSpan> src; - Vector<GMutableSpan> dst; - - Vector<OutputAttribute> dst_attributes; - - Vector<GSpan> src_no_interpolation; - Vector<GMutableSpan> dst_no_interpolation; -}; - -/** - * Gather a set of all generic attribute IDs to copy to the result curves. - */ -static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, - CurveComponent &dst_component, - AttributesForInterpolation &result) -{ - const Curves &dst_curves_id = *dst_component.get_for_read(); - const bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id.geometry); - - VectorSet<AttributeIDRef> ids; - VectorSet<AttributeIDRef> ids_no_interpolation; - src_component.attribute_foreach( - [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - return true; - } - if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { - return true; - } - if (interpolate_attribute_to_poly_curve(id)) { - ids.add_new(id); - } - else { - ids_no_interpolation.add_new(id); - } - return true; - }); - - /* Position is handled differently since it has non-generic interpolation for Bezier - * curves and because the evaluated positions are cached for each evaluated point. */ - ids.remove_contained("position"); - - retrieve_attribute_spans( - ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); - - /* Attributes that aren't interpolated like Bezier handles still have to be be copied - * to the result when there are any unselected curves of the corresponding type. */ - retrieve_attribute_spans(ids_no_interpolation, - src_component, - dst_component, - result.src_no_interpolation, - result.dst_no_interpolation, - result.dst_attributes); -} - -/** - * Copy the provided point attribute values between all curves in the #curve_ranges index - * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. - */ -template<typename T> -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> curve_ranges, - const Span<T> src, - const MutableSpan<T> dst) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { - for (const IndexRange range : curve_ranges.slice(range)) { - const IndexRange src_points = src_curves.points_for_curves(range); - const IndexRange dst_points = dst_curves.points_for_curves(range); - /* The arrays might be large, so a threaded copy might make sense here too. */ - dst.slice(dst_points).copy_from(src.slice(src_points)); - } - }); -} -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> unselected_ranges, - const GSpan src, - const GMutableSpan dst) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); - }); -} - -/** - * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. - */ -static void fill_curve_counts(const bke::CurvesGeometry &src_curves, - const Span<IndexRange> curve_ranges, - MutableSpan<int> counts) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { - for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { - for (const int i : curves_range) { - counts[i] = src_curves.points_for_curve(i).size(); - } - } - }); -} - -/** - * Turn an array of sizes into the offset at each index including all previous sizes. - */ -static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets) -{ - int total = 0; - for (const int i : counts_to_offsets.index_range().drop_back(1)) { - const int count = counts_to_offsets[i]; - BLI_assert(count > 0); - counts_to_offsets[i] = total; - total += count; - } - counts_to_offsets.last() = total; -} - -/** - * Create new curves where the selected curves have been resampled with a number of uniform-length - * samples defined by the count field. Interpolate attributes to the result, with an accuracy that - * depends on the curve's resolution parameter. - * - * \warning The values provided by the #count_field must be 1 or greater. - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_uniform_count(const CurveComponent &src_component, - const Field<bool> &selection_field, - const Field<int> &count_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - /* Create the new curves without any points and evaluate the final count directly - * into the offsets array, in order to be accumulated into offsets later. */ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.add_with_destination(count_field, dst_offsets); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - - VArray<bool> curves_cyclic = src_curves.cyclic(); - VArray<int8_t> curve_types = src_curves.curve_types(); - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - src_curves.ensure_evaluated_lengths(); - - /* Sampling arbitrary attributes works by first interpolating them to the curve's standard - * "evaluated points" and then interpolating that result with the uniform samples. This is - * potentially wasteful when down-sampling a curve to many fewer points. There are two possible - * solutions: only sample the necessary points for interpolation, or first sample curve - * parameter/segment indices and evaluate the curve directly. */ - Array<int> sample_indices(dst_curves.points_num()); - Array<float> sample_factors(dst_curves.points_num()); - - /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on - * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a - * time or one curve at a time. */ - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - Vector<std::byte> evaluated_buffer; - - /* Gather uniform samples based on the accumulated lengths of the original curve. */ - for (const int i_curve : sliced_selection) { - const bool cyclic = curves_cyclic[i_curve]; - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::create_uniform_samples( - src_curves.evaluated_lengths_for_curve(i_curve, cyclic), - curves_cyclic[i_curve], - sample_indices.as_mutable_span().slice(dst_points), - sample_factors.as_mutable_span().slice(dst_points)); - } - - /* For every attribute, evaluate attributes from every curve in the range in the original - * curve's "evaluated points", then use linear interpolation to sample to the result. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - - if (curve_types[i_curve] == CURVE_TYPE_POLY) { - length_parameterize::linear_interpolation(src.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - else { - const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); - evaluated_buffer.clear(); - evaluated_buffer.resize(sizeof(T) * evaluated_size); - MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); - src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); - - length_parameterize::linear_interpolation(evaluated.as_span(), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - } - }); - } - - /* Interpolate the evaluated positions to the resampled curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst_positions.slice(dst_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Evaluate each selected curve to its implicit evaluated points. - * - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_evaluated(const CurveComponent &src_component, - const Field<bool> &selection_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - src_curves.ensure_evaluated_offsets(); - threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { - for (const int i : selection.slice(range)) { - dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); - } - }); - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* Create the correct number of uniform-length samples for every selected curve. */ - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - /* Evaluate generic point attributes directly to the result attributes. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - src_curves.interpolate_to_evaluated( - i_curve, src.slice(src_points), dst.slice(dst_points)); - } - }); - } - - /* Copy the evaluated positions to the selected curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Create a resampled curve point count field for both "uniform" options. - * The complexity is handled here in order to make the actual resampling functions simpler. - */ -static Field<int> get_curve_count_field(GeoNodeExecParams params, - const GeometryNodeCurveResampleMode mode) -{ - if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - static fn::CustomMF_SI_SO<int, int> max_one_fn( - "Clamp Above One", - [](int value) { return std::max(1, value); }, - fn::CustomMF_presets::AllSpanOrSingle()); - auto clamp_op = std::make_shared<FieldOperation>( - FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))})); - - return Field<int>(std::move(clamp_op)); - } - - if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( - "Length Input to Count", - [](const float curve_length, const float sample_length) { - /* Find the number of sampled segments by dividing the total length by - * the sample length. Then there is one more sampled point than segment. */ - const int count = int(curve_length / sample_length) + 1; - return std::max(1, count); - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto get_count_op = std::make_shared<FieldOperation>( - FieldOperation(get_count_fn, - {Field<float>(std::make_shared<SplineLengthFieldInput>()), - params.extract_input<Field<float>>("Length")})); - - return Field<int>(std::move(get_count_op)); - } - - BLI_assert_unreachable(); - return {}; -} - -/** - * Create a selection field that removes curves without any evaluated points (invalid NURBS curves) - * from the original selection provided to the node. This is here to simplify the sampling actual - * resampling code. - */ -static Field<bool> get_selection_field(GeoNodeExecParams params) -{ - static fn::CustomMF_SI_SI_SO<bool, int, bool> get_selection_fn( - "Create Curve Selection", - [](const bool orig_selection, const int evaluated_points_num) { - return orig_selection && evaluated_points_num > 1; - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto selection_op = std::make_shared<FieldOperation>( - FieldOperation(get_selection_fn, - {params.extract_input<Field<bool>>("Selection"), - Field<int>(std::make_shared<EvaluatedCountFieldInput>())})); - - return Field<bool>(std::move(selection_op)); -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -613,35 +58,38 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurveResample &storage = node_storage(params.node()); const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - const Field<bool> selection = get_selection_field(params); + const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_count(*component, selection, count)); + } + } + }); + break; + } case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - Field<int> count = get_curve_count_field(params, mode); - - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + Field<int> 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()) { + geometry.replace_curves(geometry::resample_to_length(*component, selection, length)); + } } - - Curves *result = resample_to_uniform_count( - *geometry_set.get_component_for_read<CurveComponent>(), selection, count); - - geometry_set.replace_curves(result); }); break; } case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_evaluated(*component, selection)); + } } - - Curves *result = resample_to_evaluated( - *geometry_set.get_component_for_read<CurveComponent>(), selection); - - geometry_set.replace_curves(result); }); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index de29735bd2d..64a174df599 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index 5d97720a4f8..a548becf24e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -144,9 +144,9 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry return {}; } -static VArray<float> construct_curve_length_varray(const bke::CurvesGeometry &curves, - const IndexMask UNUSED(mask), - const AttributeDomain domain) +static VArray<float> construct_curve_length_parameter_varray(const bke::CurvesGeometry &curves, + const IndexMask UNUSED(mask), + const AttributeDomain domain) { curves.ensure_evaluated_lengths(); @@ -217,9 +217,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { } }; -class CurveLengthFieldInput final : public GeometryFieldInput { +class CurveLengthParameterFieldInput final : public GeometryFieldInput { public: - CurveLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") + CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") { category_ = Category::Generated; } @@ -233,7 +233,7 @@ class CurveLengthFieldInput final : public GeometryFieldInput { if (curve_component.has_curves()) { const Curves &curves_id = *curve_component.get_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_length_varray(curves, mask, domain); + return construct_curve_length_parameter_varray(curves, mask, domain); } } return {}; @@ -247,7 +247,7 @@ class CurveLengthFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; + return dynamic_cast<const CurveLengthParameterFieldInput *>(&other) != nullptr; } }; @@ -288,7 +288,7 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()}; - Field<float> length_field{std::make_shared<CurveLengthFieldInput>()}; + Field<float> length_field{std::make_shared<CurveLengthParameterFieldInput>()}; Field<int> index_on_spline_field{std::make_shared<IndexOnSplineFieldInput>()}; params.set_output("Factor", std::move(parameter_field)); params.set_output("Length", std::move(length_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 6e45411a9af..500804e41f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -107,20 +107,20 @@ static void copy_attributes(const Spline &input_spline, Spline &output_spline, C static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions, const KnotsMode knots_mode) { - const int nurbs_positions_size = nurbs_positions.size(); + const int nurbs_positions_num = nurbs_positions.size(); Vector<float3> handle_positions; if (knots_mode == NURBS_KNOT_MODE_BEZIER) { - for (const int i : IndexRange(nurbs_positions_size)) { + for (const int i : IndexRange(nurbs_positions_num)) { if (i % 3 == 1) { continue; } handle_positions.append(nurbs_positions[i]); } - if (nurbs_positions_size % 3 == 1) { + if (nurbs_positions_num % 3 == 1) { handle_positions.pop_last(); } - else if (nurbs_positions_size % 3 == 2) { - const int last_index = nurbs_positions_size - 1; + else if (nurbs_positions_num % 3 == 2) { + const int last_index = nurbs_positions_num - 1; handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]); } } @@ -134,11 +134,11 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]); handle_positions.append(nurbs_positions[1]); } - const int segments_size = nurbs_positions_size - 1; - const bool ignore_interior_segment = segments_size == 3 && is_periodic == false; + const int segments_num = nurbs_positions_num - 1; + const bool ignore_interior_segment = segments_num == 3 && is_periodic == false; if (ignore_interior_segment == false) { - const float mid_offset = (float)(segments_size - 1) / 2.0f; - for (const int i : IndexRange(1, segments_size - 2)) { + const float mid_offset = (float)(segments_num - 1) / 2.0f; + for (const int i : IndexRange(1, segments_num - 2)) { const int divisor = is_periodic ? 3 : std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f)); @@ -151,7 +151,7 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po } } } - const int last_index = nurbs_positions_size - 1; + const int last_index = nurbs_positions_num - 1; if (is_periodic) { handle_positions.append( nurbs_positions[last_index - 1] + @@ -368,11 +368,11 @@ static void node_geo_exec(GeoNodeExecParams params) const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( *curve_component->get_for_read()); GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; - const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = curve_component->attribute_domain_num(ATTR_DOMAIN_CURVE); Span<SplinePtr> src_splines = curve->splines(); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 371556c04f1..4d8745bf79e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -30,9 +30,9 @@ static Array<int> get_subdivided_offsets(const Spline &spline, const VArray<int> &cuts, const int spline_offset) { - Array<int> offsets(spline.segments_size() + 1); + Array<int> offsets(spline.segments_num() + 1); int offset = 0; - for (const int i : IndexRange(spline.segments_size())) { + for (const int i : IndexRange(spline.segments_num())) { offsets[i] = offset; offset = offset + std::max(cuts[spline_offset + i], 0) + 1; } @@ -46,8 +46,8 @@ static void subdivide_attribute(Span<T> src, const bool is_cyclic, MutableSpan<T> dst) { - const int src_size = src.size(); - threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + const int src_num = src.size(); + threading::parallel_for(IndexRange(src_num - 1), 1024, [&](IndexRange range) { for (const int i : range) { const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src[i]; @@ -60,7 +60,7 @@ static void subdivide_attribute(Span<T> src, }); if (is_cyclic) { - const int i = src_size - 1; + const int i = src_num - 1; const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src.last(); const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; @@ -86,7 +86,7 @@ static void subdivide_attribute(Span<T> src, static void subdivide_bezier_segment(const BezierSpline &src, const int index, const int offset, - const int result_size, + const int result_num, Span<float3> src_positions, Span<float3> src_handles_left, Span<float3> src_handles_right, @@ -106,11 +106,11 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR); - const float factor_delta = 1.0f / result_size; - for (const int cut : IndexRange(result_size)) { + const float factor_delta = 1.0f / result_num; + for (const int cut : IndexRange(result_num)) { const float factor = cut * factor_delta; dst_positions[offset + cut] = attribute_math::mix2( factor, src_positions[index], src_positions[next_index]); @@ -120,10 +120,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE); - const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num; /* Create a Bezier segment to update iteratively for every subdivision * and references to the meaningful values for ease of use. */ @@ -138,8 +138,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, handle_prev = src_handles_right[index]; handle_next = src_handles_left[next_index]; - for (const int cut : IndexRange(result_size - 1)) { - const float parameter = 1.0f / (result_size - cut); + for (const int cut : IndexRange(result_num - 1)) { + const float parameter = 1.0f / (result_num - cut); const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); /* Copy relevant temporary data to the result. */ @@ -154,7 +154,7 @@ static void subdivide_bezier_segment(const BezierSpline &src, } /* Copy the handles for the last segment from the temporary spline. */ - dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_right[offset + result_num - 1] = handle_prev; dst_handles_left[i_segment_last] = handle_next; } } @@ -287,9 +287,9 @@ static SplinePtr subdivide_spline(const Spline &spline, * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); - const int result_size = offsets.last() + int(!spline.is_cyclic()); + const int result_num = offsets.last() + int(!spline.is_cyclic()); SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(result_size); + new_spline->resize(result_num); subdivide_builtin_attributes(spline, offsets, *new_spline); subdivide_dynamic_attributes(spline, offsets, *new_spline); return new_spline; @@ -334,9 +334,9 @@ static void node_geo_exec(GeoNodeExecParams params) const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(cuts_field); evaluator.evaluate(); const VArray<int> &cuts = evaluator.get_evaluated<int>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 894580f2932..b45a0d6c81a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -90,7 +90,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += count; } } @@ -104,7 +104,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += splines[i]->length() / resolution + 1; } } @@ -240,18 +240,18 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, for (const int i : range) { const Spline &spline = *splines[i]; const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size == 0) { + const int num = offsets[i + 1] - offsets[i]; + if (num == 0) { continue; } - const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); + const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), uniform_samples, - data.radii.slice(offset, size)); + data.radii.slice(offset, num)); for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; @@ -260,14 +260,13 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), - uniform_samples, - dst.slice(offset, size)); + spline.sample_with_index_factors( + spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); } if (!data.tangents.is_empty()) { Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size); + MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); for (float3 &vector : sampled_tangents) { vector = math::normalize(vector); @@ -276,7 +275,7 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, if (!data.normals.is_empty()) { Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, size); + MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); for (float3 &vector : sampled_normals) { vector = math::normalize(vector); @@ -298,8 +297,8 @@ static void copy_spline_domain_attributes(const CurveEval &curve, for (const int i : curve.splines().index_range()) { const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], size); + const int num = offsets[i + 1] - offsets[i]; + type.fill_assign_n(curve_attribute[i], dst[offset], num); } return true; @@ -329,13 +328,13 @@ static void node_geo_exec(GeoNodeExecParams params) curve->assert_valid_point_attributes(); const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_size = offsets.last(); - if (total_size == 0) { + const int total_num = offsets.last(); + if (total_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( points, *curve, attribute_outputs); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index df360818313..c993a3d305d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -117,10 +117,10 @@ struct TrimLocation { }; template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size) +static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) { - BLI_assert(start_index + size - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * size); + BLI_assert(start_index + num - 1 <= data.size()); + memmove(data.data(), &data[start_index], sizeof(T) * num); } /* Shift slice to start of span and modifies start and end data. */ @@ -129,17 +129,17 @@ static void linear_trim_data(const TrimLocation &start, const TrimLocation &end, MutableSpan<T> data) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, size); + shift_slice_to_start<T>(data, start.left_index, num); } const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]); + const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); data.first() = start_data; - data[size - 1] = end_data; + data[num - 1] = end_data; } /** @@ -152,12 +152,12 @@ static void linear_trim_to_output_data(const TrimLocation &start, Span<T> src, MutableSpan<T> dst) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - dst.copy_from(src.slice(start.left_index, size)); + dst.copy_from(src.slice(start.left_index, num)); dst.first() = start_data; dst.last() = end_data; } @@ -175,8 +175,8 @@ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lo const int right = left == (spline.size() - 1) ? 0 : left + 1; const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_size = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); + const int segment_eval_num = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); return {left, right, factor}; } @@ -191,7 +191,7 @@ static void trim_poly_spline(Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; linear_trim_data<float3>(start, end, spline.positions()); linear_trim_data<float>(start, end, spline.radii()); @@ -209,7 +209,7 @@ static void trim_poly_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - spline.resize(size); + spline.resize(num); } /** @@ -225,11 +225,11 @@ static PolySpline trim_nurbs_spline(const Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Create poly spline and copy trimmed data to it. */ PolySpline new_spline; - new_spline.resize(size); + new_spline.resize(num); /* Copy generic attribute data. */ spline.attributes.foreach_attribute( @@ -283,7 +283,7 @@ static void trim_bezier_spline(Spline &spline, const Span<int> control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Trim the spline attributes. Done before end.factor recalculation as it needs * the original end.factor value. */ @@ -301,10 +301,10 @@ static void trim_bezier_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - /* Recalculate end.factor if the size is two, because the adjustment in the + /* Recalculate end.factor if the `num` is two, because the adjustment in the * position of the control point of the spline to the left of the new end point will change the * factor between them. */ - if (size == 2) { + if (num == 2) { if (start_lookup.factor == 1.0f) { end.factor = 0.0f; } @@ -328,38 +328,38 @@ static void trim_bezier_spline(Spline &spline, const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( end.left_index, end.right_index, end.factor); - /* If size is two, then the start point right handle needs to change to reflect the end point + /* If `num` is two, then the start point right handle needs to change to reflect the end point * previous handle update. */ - if (size == 2) { + if (num == 2) { start_point.right_handle = end_point.handle_prev; } /* Shift control point position data to start at beginning of array. */ if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size); + shift_slice_to_start(bezier_spline.positions(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); } bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[size - 1] = end_point.position; + bezier_spline.positions()[num - 1] = end_point.position; bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle; + bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle; + bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; /* If there is at least one control point between the endpoints, update the control * point handle to the right of the start point and to the left of the end point. */ - if (size > 2) { + if (num > 2) { bezier_spline.handle_positions_left()[start.right_index - start.left_index] = start_point.handle_next; bezier_spline.handle_positions_right()[end.left_index - start.left_index] = end_point.handle_prev; } - bezier_spline.resize(size); + bezier_spline.resize(num); } static void trim_spline(SplinePtr &spline, @@ -506,9 +506,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); @@ -527,7 +527,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, continue; } - if (spline->evaluated_edges_size() == 0) { + if (spline->evaluated_edges_num() == 0) { continue; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 8a0c900fbde..99edc4d298c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -470,7 +470,7 @@ static void separate_curve_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -493,7 +493,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; fn::FieldEvaluator selection_evaluator{field_context, - src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + src_points.attribute_domain_num(ATTR_DOMAIN_POINT)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -526,8 +526,8 @@ static void separate_instance_selection(GeometrySet &geometry_set, InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0); @@ -1238,7 +1238,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index d9f29d1ef1c..c242cfd5cf3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -398,11 +398,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent { const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER; GeometryComponentFieldContext field_context{component, attribute_domain}; - const int domain_size = component.attribute_domain_size(attribute_domain); + const int domain_num = component.attribute_domain_num(attribute_domain); - Array<float> densities(domain_size, 0.0f); + Array<float> densities(domain_num, 0.0f); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(density_field, densities.as_mutable_span()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 1b26cfe31fe..ee3de995cb1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -351,19 +351,19 @@ static void duplicate_curves(GeometrySet &geometry_set, Array<int> curve_offsets(selection.size() + 1); Array<int> point_offsets(selection.size() + 1); - int dst_curves_size = 0; - int dst_points_size = 0; + int dst_curves_num = 0; + int dst_points_num = 0; for (const int i_curve : selection.index_range()) { const int count = std::max(counts[selection[i_curve]], 0); - curve_offsets[i_curve] = dst_curves_size; - point_offsets[i_curve] = dst_points_size; - dst_curves_size += count; - dst_points_size += count * curves.points_for_curve(selection[i_curve]).size(); + curve_offsets[i_curve] = dst_curves_num; + point_offsets[i_curve] = dst_points_num; + dst_curves_num += count; + dst_points_num += count * curves.points_for_curve(selection[i_curve]).size(); } - curve_offsets.last() = dst_curves_size; - point_offsets.last() = dst_points_size; + curve_offsets.last() = dst_curves_num; + point_offsets.last() = dst_points_num; - Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_curves_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write(); @@ -379,7 +379,7 @@ static void duplicate_curves(GeometrySet &geometry_set, } } }); - all_dst_offsets.last() = dst_points_size; + all_dst_offsets.last() = dst_points_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -807,7 +807,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); - const int dst_size = offsets.last(); + const int dst_num = offsets.last(); Array<int> point_to_curve_map(src_curves.points_num()); threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) { @@ -817,13 +817,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set, } }); - Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write(); for (const int i : new_curves.curves_range()) { new_curve_offsets[i] = i; } - new_curve_offsets.last() = dst_size; + new_curve_offsets.last() = dst_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -940,10 +940,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, { const PointCloudComponent &src_points = *geometry_set.get_component_for_read<PointCloudComponent>(); - const int point_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, point_size}; + FieldEvaluator evaluator{field_context, point_num}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -1026,7 +1026,7 @@ static void duplicate_instances(GeometrySet &geometry_set, *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; - FieldEvaluator evaluator{field_context, src_instances.instances_amount()}; + FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 84acab47661..94fbec66bfe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; - const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 77be98f169e..bf956f3b83d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput { { const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; FieldEvaluator value_evaluator{value_field_context, - component.attribute_domain_size(value_field_domain_)}; + component.attribute_domain_num(value_field_domain_)}; value_evaluator.add(value_field_); value_evaluator.evaluate(); const GVArray &values = value_evaluator.get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 34c3169065c..0484017cf3b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b) static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index d39291a2a7e..8e3a9b6769d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -101,8 +101,7 @@ class IslandCountFieldInput final : public GeometryFieldInput { island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - mesh_component.attribute_domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 6c24f86b63b..84d773ff8eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -4,71 +4,6 @@ #include "BKE_curves.hh" -namespace blender::nodes { - -/* -------------------------------------------------------------------- - * Spline Length - */ - -static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, - const AttributeDomain domain) -{ - if (!component.has_curves()) { - return {}; - } - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - - curves.ensure_evaluated_lengths(); - - VArray<bool> cyclic = curves.cyclic(); - VArray<float> lengths = VArray<float>::ForFunc( - curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { - return curves.evaluated_length_total_for_curve(index, cyclic[index]); - }); - - if (domain == ATTR_DOMAIN_CURVE) { - return lengths; - } - - if (domain == ATTR_DOMAIN_POINT) { - return component.attribute_try_adapt_domain<float>( - std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); - } - - return {}; -} - -SplineLengthFieldInput::SplineLengthFieldInput() - : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") -{ - category_ = Category::Generated; -} - -GVArray SplineLengthFieldInput::get_varray_for_context(const GeometryComponent &component, - const AttributeDomain domain, - IndexMask UNUSED(mask)) const -{ - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_length_gvarray(curve_component, domain); - } - return {}; -} - -uint64_t SplineLengthFieldInput::hash() const -{ - /* Some random constant hash. */ - return 3549623580; -} - -bool SplineLengthFieldInput::is_equal_to(const fn::FieldNode &other) const -{ - return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr; -} - -} // namespace blender::nodes - namespace blender::nodes::node_geo_input_spline_length_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -81,8 +16,8 @@ static void node_declare(NodeDeclarationBuilder &b) * Spline Count */ -static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, - const AttributeDomain domain) +static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component, + const AttributeDomain domain) { if (!component.has_curves()) { return {}; @@ -117,7 +52,7 @@ class SplineCountFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_count_gvarray(curve_component, domain); + return construct_curve_point_count_gvarray(curve_component, domain); } return {}; } @@ -136,7 +71,7 @@ class SplineCountFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { - Field<float> spline_length_field{std::make_shared<SplineLengthFieldInput>()}; + Field<float> spline_length_field{std::make_shared<bke::CurveLengthFieldInput>()}; Field<int> spline_count_field{std::make_shared<SplineCountFieldInput>()}; params.set_output("Length", std::move(spline_length_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 61f719ade4e..12582f9e9c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -50,7 +50,7 @@ static void add_instances_from_component( const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate) { const AttributeDomain domain = ATTR_DOMAIN_POINT; - const int domain_size = src_component.attribute_domain_size(domain); + const int domain_num = src_component.attribute_domain_num(domain); VArray<bool> pick_instance; VArray<int> indices; @@ -59,7 +59,7 @@ static void add_instances_from_component( GeometryComponentFieldContext field_context{src_component, domain}; const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ @@ -73,7 +73,7 @@ static void add_instances_from_component( /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ - const int start_len = dst_component.instances_amount(); + const int start_len = dst_component.instances_num(); const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); @@ -119,12 +119,12 @@ static void add_instances_from_component( const bool use_individual_instance = pick_instance[i]; if (use_individual_instance) { if (src_instances != nullptr) { - const int src_instances_amount = src_instances->instances_amount(); + const int src_instances_num = src_instances->instances_num(); const int original_index = indices[i]; /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 * refers to the last element. */ - const int index = mod_i(original_index, std::max(src_instances_amount, 1)); - if (index < src_instances_amount) { + const int index = mod_i(original_index, std::max(src_instances_num, 1)); + if (index < src_instances_num) { /* Get the reference to the source instance. */ const int src_handle = src_instances->instance_reference_handles()[index]; dst_handle = handle_mapping[src_handle]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index e35f152ce49..2126a5cc329 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -40,9 +40,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(std::move(selection_field)); evaluator.add(std::move(position_field)); evaluator.add(std::move(radius_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 0e2803cd035..2067086c298 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -56,8 +56,8 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, int offset = 0; for (const GeometryComponent *component : src_components) { - const int domain_size = component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component->attribute_domain_num(domain); + if (domain_num == 0) { continue; } GVArray read_attribute = component->attribute_get_for_read( @@ -66,9 +66,9 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, GVArray_GSpan src_span{read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; - cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size); + cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num); - offset += domain_size; + offset += domain_num; } } @@ -101,7 +101,7 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_amount(); + tot_instances += src_component->instances_num(); } dst_component.reserve(tot_instances); diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index 1ec97858d4d..1def4089115 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -39,9 +39,9 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p const float merge_distance, const Field<bool> &selection_field) { - const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); @@ -57,10 +57,10 @@ static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponen const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - Array<bool> selection(src_size); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + Array<bool> selection(src_num); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add_with_destination(selection_field, selection.as_mutable_span()); evaluator.evaluate(); @@ -72,9 +72,9 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 636ecb8ab41..0029b547375 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -6,414 +6,9 @@ #include "BKE_material.h" #include "BKE_mesh.h" -#include "node_geometry_util.hh" - -namespace blender::nodes { - -struct CuboidConfig { - float3 size; - int verts_x; - int verts_y; - int verts_z; - int edges_x; - int edges_y; - int edges_z; - int vertex_count; - int poly_count; - int loop_count; - - CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) - : size(size), - verts_x(verts_x), - verts_y(verts_y), - verts_z(verts_z), - edges_x(verts_x - 1), - edges_y(verts_y - 1), - edges_z(verts_z - 1) - { - BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); - this->vertex_count = this->get_vertex_count(); - this->poly_count = this->get_poly_count(); - this->loop_count = this->poly_count * 4; - } - - private: - int get_vertex_count() - { - const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); - return verts_x * verts_y * verts_z - inner_position_count; - } - - int get_poly_count() - { - return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); - } -}; - -static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) -{ - const float z_bottom = -config.size.z / 2.0f; - const float z_delta = config.size.z / config.edges_z; - - const float x_left = -config.size.x / 2.0f; - const float x_delta = config.size.x / config.edges_x; - - const float y_front = -config.size.y / 2.0f; - const float y_delta = config.size.y / config.edges_y; - - int vert_index = 0; - - for (const int z : IndexRange(config.verts_z)) { - if (ELEM(z, 0, config.edges_z)) { - /* Fill bottom and top. */ - const float z_pos = z_bottom + z_delta * z; - for (const int y : IndexRange(config.verts_y)) { - const float y_pos = y_front + y_delta * y; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - } - else { - for (const int y : IndexRange(config.verts_y)) { - if (ELEM(y, 0, config.edges_y)) { - /* Fill y-sides. */ - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - else { - /* Fill x-sides. */ - const float x_pos = x_left; - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - const float x_pos2 = x_left + x_delta * config.edges_x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); - } - } - } - } -} - -/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. - * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for - * anti-clockwise. - */ -static void define_quad(MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops, - const int poly_index, - const int loop_index, - const int vert_1, - const int vert_2, - const int vert_3, - const int vert_4) -{ - MPoly &poly = polys[poly_index]; - poly.loopstart = loop_index; - poly.totloop = 4; - - MLoop &loop_1 = loops[loop_index]; - loop_1.v = vert_1; - MLoop &loop_2 = loops[loop_index + 1]; - loop_2.v = vert_2; - MLoop &loop_3 = loops[loop_index + 2]; - loop_3.v = vert_3; - MLoop &loop_4 = loops[loop_index + 3]; - loop_4.v = vert_4; -} - -static void calculate_polys(const CuboidConfig &config, - MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops) -{ - int loop_index = 0; - int poly_index = 0; - - /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ - const int xy_cross_section_vert_count = config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2); +#include "GEO_mesh_primitive_cuboid.hh" - /* Calculate polys for Bottom faces. */ - int vert_1_start = 0; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - const int vert_1 = vert_1_start + x; - const int vert_2 = vert_1_start + config.verts_x + x; - const int vert_3 = vert_2 + 1; - const int vert_4 = vert_1 + 1; - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - vert_1_start += config.verts_x; - } - - /* Calculate polys for Front faces. */ - vert_1_start = 0; - int vert_2_start = config.verts_x * config.verts_y; - - for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_1_start = vert_2_start; - vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); - } - - /* Calculate polys for Top faces. */ - vert_1_start = config.verts_x * config.verts_y + - (config.verts_z - 2) * (config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2)); - vert_2_start = vert_1_start + config.verts_x; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_2_start += config.verts_x; - vert_1_start += config.verts_x; - } - - /* Calculate polys for Back faces. */ - vert_1_start = config.verts_x * config.edges_y; - vert_2_start = vert_1_start + xy_cross_section_vert_count; - - for (const int z : IndexRange(config.edges_z)) { - if (z == (config.edges_z - 1)) { - vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); - } - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_2_start + x, - vert_2_start + x + 1, - vert_1_start + x + 1); - loop_index += 4; - poly_index++; - } - vert_2_start += xy_cross_section_vert_count; - vert_1_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Left faces. */ - vert_1_start = 0; - vert_2_start = config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1; - int vert_2; - int vert_3; - int vert_4; - - if (z == 0 || y == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_1 += config.verts_x - 2; - vert_4 = vert_1 + 2; - } - - if (y == 0 || z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_2 += config.verts_x - 2; - vert_3 = vert_2 + 2; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Right faces. */ - vert_1_start = config.edges_x; - vert_2_start = vert_1_start + config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1 = vert_1_start; - int vert_2 = vert_2_start; - int vert_3 = vert_2_start + 2; - int vert_4 = vert_1 + config.verts_x; - - if (z == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_4 = vert_1 + 2; - } - - if (z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_3 = vert_2 + 2; - } - - if (y == (config.edges_y - 1)) { - vert_3 = vert_2 + config.verts_x; - vert_4 = vert_1 + config.verts_x; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } -} - -static void calculate_uvs(const CuboidConfig &config, Mesh *mesh) -{ - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttribute_Typed<float2> uv_attribute = - mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); - MutableSpan<float2> uvs = uv_attribute.as_span(); - - int loop_index = 0; - - const float x_delta = 0.25f / static_cast<float>(config.edges_x); - const float y_delta = 0.25f / static_cast<float>(config.edges_y); - const float z_delta = 0.25f / static_cast<float>(config.edges_z); - - /* Calculate bottom face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); - } - } - - /* Calculate front face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); - } - } - - /* Calculate top face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); - } - } - - /* Calculate back face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); - } - } - - /* Calculate left face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); - } - } - - /* Calculate right face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); - } - } - - uv_attribute.save(); -} - -Mesh *create_cuboid_mesh(const float3 size, - const int verts_x, - const int verts_y, - const int verts_z) -{ - const CuboidConfig config(size, verts_x, verts_y, verts_z); - - Mesh *mesh = BKE_mesh_new_nomain( - config.vertex_count, 0, 0, config.loop_count, config.poly_count); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - - calculate_vertices(config, {mesh->mvert, mesh->totvert}); - - calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); - BKE_mesh_calc_edges(mesh, false, false); - - calculate_uvs(config, mesh); - - return mesh; -} - -} // namespace blender::nodes +#include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_primitive_cube_cc { @@ -442,6 +37,16 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } +static Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + Mesh *mesh = geometry::create_cuboid_mesh(size, verts_x, verts_y, verts_z, "uv_map"); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + return mesh; +} + static Mesh *create_cube_mesh(const float3 size, const int verts_x, const int verts_y, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index f6ee3d00dee..ec6acf55dd8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)}; evaluator.add(params.get_input<Field<bool>>("Selection")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 1a0cc53cb6c..6b23b685549 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" @@ -41,14 +43,15 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -template<typename T> -static void copy_attribute_to_points(const VArray<T> &src, - const IndexMask mask, - MutableSpan<T> dst) +static void materialize_compressed_to_uninitialized_threaded(const GVArray &src, + const IndexMask mask, + GMutableSpan dst) { - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } + BLI_assert(src.type() == dst.type()); + BLI_assert(mask.size() == dst.size()); + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data()); + }); } static void geometry_set_mesh_to_points(GeometrySet &geometry_set, @@ -63,12 +66,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, return; } GeometryComponentFieldContext field_context{*mesh_component, domain}; - const int domain_size = mesh_component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = mesh_component->attribute_domain_num(domain); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Evaluating directly into the point cloud doesn't work because we are not using the full * "min_array_size" array but compressing the selected elements into the final array with no @@ -79,16 +82,21 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); geometry_set.replace_pointcloud(pointcloud); PointCloudComponent &point_component = geometry_set.get_component_for_write<PointCloudComponent>(); - copy_attribute_to_points(evaluator.get_evaluated<float3>(0), - selection, - {(float3 *)pointcloud->co, pointcloud->totpoint}); - copy_attribute_to_points( - evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint}); + OutputAttribute position = point_component.attribute_try_get_for_output_only( + "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + materialize_compressed_to_uninitialized_threaded( + evaluator.get_evaluated(0), selection, position.as_span()); + position.save(); + + OutputAttribute radius = point_component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT); + materialize_compressed_to_uninitialized_threaded( + evaluator.get_evaluated(1), selection, radius.as_span()); + radius.save(); Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( @@ -102,11 +110,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, OutputAttribute dst = point_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray<T> src_typed = src.typed<T>(); - copy_attribute_to_points(src_typed, selection, dst.as_span().typed<T>()); - }); + materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span()); dst.save(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 0f6586617bc..577b001fd06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -38,13 +38,13 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, } GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; - const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index c99b51ffd4c..42cee4c0efe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -168,14 +168,14 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms, Field<float> radius_field = params.get_input<Field<float>>("Radius"); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - r_positions.resize(r_positions.size() + domain_size); - positions.materialize(r_positions.as_mutable_span().take_back(domain_size)); + r_positions.resize(r_positions.size() + domain_num); + positions.materialize(r_positions.as_mutable_span().take_back(domain_num)); - r_radii.resize(r_radii.size() + domain_size); - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + r_radii.resize(r_radii.size() + domain_num); + fn::FieldEvaluator evaluator{field_context, domain_num}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_num)); evaluator.evaluate(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 368954447c9..0c30d50076f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -312,8 +312,8 @@ class RaycastFunction : public fn::MultiFunction { } const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); target_data_ = &target_evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 6eb95859e50..59e203afd08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -19,9 +19,9 @@ static void node_declare(NodeDeclarationBuilder &b) static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances_component.instances_amount(); + const int domain_num = instances_component.instances_num(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 4ca21874f8f..d4716a6b6f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -23,7 +23,7 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 73e49c7d037..d2082924fa7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -75,12 +75,12 @@ static void set_position_in_component(CurveComponent &component, const Field<float3> &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index a23a6c09551..4c84093bfcb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 1155c97dc38..8b1e5935a61 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -17,15 +17,15 @@ static void set_tilt_in_component(GeometryComponent &component, const Field<float> &tilt_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>( "tilt", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(tilt_field, tilts.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index 0892e068ce2..ec95f9a89f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -20,12 +20,12 @@ static void set_id_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Since adding the ID attribute can change the result of the field evaluation (the random value diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index a0b38209f97..58613dae832 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -17,15 +17,15 @@ static void set_material_index_in_component(GeometryComponent &component, const Field<int> &index_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>( "material_index", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(index_field, indices.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 93024fd81d6..571bead9743 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index d2ff9753897..caf33108716 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -143,12 +143,12 @@ static void set_position_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index 3420e17cc10..b98fbd0a0fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -17,15 +17,15 @@ static void set_smooth_in_component(GeometryComponent &component, const Field<bool> &shade_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>( "shade_smooth", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(shade_field, shades.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index dc7f3b1343a..976857883f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -17,15 +17,15 @@ static void set_cyclic_in_component(GeometryComponent &component, const Field<bool> &cyclic_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>( "cyclic", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(cyclic_field, cyclics.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index f3031ff3678..8b665376c01 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -17,15 +17,15 @@ static void set_resolution_in_component(GeometryComponent &component, const Field<int> &resolution_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>( "resolution", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(resolution_field, resolutions.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index de206be5367..3b348dd0136 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -88,8 +88,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CPPType &type = field.cpp_type(); const CustomDataType data_type = bke::cpp_type_to_custom_data_type(type); @@ -97,10 +97,10 @@ static void try_capture_field_on_geometry(GeometryComponent &component, /* Could avoid allocating a new buffer if: * - We are writing to an attribute that exists already. * - The field does not depend on that attribute (we can't easily check for that yet). */ - void *buffer = MEM_mallocN(type.size() * domain_size, __func__); + void *buffer = MEM_mallocN(type.size() * domain_num, __func__); fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size}); + evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num}); evaluator.evaluate(); component.attribute_try_delete(name); @@ -114,7 +114,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component, else { /* Cannot change type of built-in attribute. */ } - type.destruct_n(buffer, domain_size); + type.destruct_n(buffer, domain_num); MEM_freeN(buffer); } else { diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 4832feac5bd..9eda5bb34ff 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -21,7 +23,13 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); - b.add_input<decl::Float>(N_("Crease")) + b.add_input<decl::Float>(N_("Edge Crease")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .supports_field() + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Vertex Crease")) .default_value(0.0f) .min(0.0f) .max(1.0f) @@ -44,6 +52,45 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } +#ifdef WITH_OPENSUBDIV +static void materialize_and_clamp_creases(const VArray<float> &crease_varray, + MutableSpan<float> creases) +{ + threading::parallel_for(creases.index_range(), 1024, [&](IndexRange range) { + crease_varray.materialize(range, creases); + for (const int i : range) { + creases[i] = std::clamp(creases[i], 0.0f, 1.0f); + } + }); +} + +static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray) +{ + float *crease; + if (CustomData_has_layer(&mesh.vdata, CD_CREASE)) { + crease = static_cast<float *>(CustomData_get_layer(&mesh.vdata, CD_CREASE)); + } + else { + crease = static_cast<float *>( + CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert)); + } + materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert}); +} + +static void write_edge_creases(MeshComponent &mesh, const VArray<float> &crease_varray) +{ + OutputAttribute_Typed<float> attribute = mesh.attribute_try_get_for_output_only<float>( + "crease", ATTR_DOMAIN_EDGE); + materialize_and_clamp_creases(crease_varray, attribute.as_span()); + attribute.save(); +} + +static bool varray_is_nonzero(const VArray<float> &varray) +{ + return !(varray.is_single() && varray.get_internal_single() == 0.0f); +} +#endif + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); @@ -51,7 +98,8 @@ static void node_geo_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else - Field<float> crease_field = params.extract_input<Field<float>>("Crease"); + Field<float> edge_crease_field = params.extract_input<Field<float>>("Edge Crease"); + Field<float> vertex_crease_field = params.extract_input<Field<float>>("Vertex Crease"); const NodeGeometrySubdivisionSurface &storage = node_storage(params.node()); const int uv_smooth = storage.uv_smooth; @@ -69,27 +117,31 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - AttributeDomain domain = ATTR_DOMAIN_EDGE; - GeometryComponentFieldContext field_context{mesh_component, domain}; - const int domain_size = mesh_component.attribute_domain_size(domain); - - if (domain_size == 0) { + const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); + const int verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + const int edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + if (verts_num == 0 || edges_num == 0) { return; } - FieldEvaluator evaluator(field_context, domain_size); - evaluator.add(crease_field); - evaluator.evaluate(); - const VArray<float> &creases = evaluator.get_evaluated<float>(0); + GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT}; + FieldEvaluator point_evaluator(point_context, verts_num); + point_evaluator.add(vertex_crease_field); + point_evaluator.evaluate(); + const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0); + + GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator(edge_context, edges_num); + edge_evaluator.add(edge_crease_field); + edge_evaluator.evaluate(); + const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0); + + const bool use_creases = varray_is_nonzero(vertex_creases) || varray_is_nonzero(edge_creases); - OutputAttribute_Typed<float> crease = mesh_component.attribute_try_get_for_output_only<float>( - "crease", domain); - MutableSpan<float> crease_span = crease.as_span(); - for (auto i : creases.index_range()) { - crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); + if (use_creases) { + write_vertex_creases(*geometry_set.get_mesh_for_write(), vertex_creases); + write_edge_creases(geometry_set.get_component_for_write<MeshComponent>(), edge_creases); } - crease.save(); /* Initialize mesh settings. */ SubdivToMeshSettings mesh_settings; @@ -100,7 +152,7 @@ static void node_geo_exec(GeoNodeExecParams params) SubdivSettings subdiv_settings; subdiv_settings.is_simple = false; subdiv_settings.is_adaptive = false; - subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); + subdiv_settings.use_creases = use_creases; subdiv_settings.level = subdiv_level; subdiv_settings.vtx_boundary_interpolation = @@ -108,19 +160,19 @@ static void node_geo_exec(GeoNodeExecParams params) subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( uv_smooth); - Mesh *mesh_in = mesh_component.get_for_write(); + const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in); /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { return; } - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in); - mesh_component.replace(mesh_out); + geometry_set.replace_mesh(mesh_out); BKE_subdiv_free(subdiv); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 12e306ba480..dca214660c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -365,7 +365,7 @@ static bool component_is_available(const GeometrySet &geometry, if (component.is_empty()) { return false; } - return component.attribute_domain_size(domain) != 0; + return component.attribute_domain_num(domain) != 0; } /** @@ -433,8 +433,8 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { { const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num); source_evaluator_->add(src_field_); source_evaluator_->evaluate(); source_data_ = &source_evaluator_->get_evaluated(0); @@ -578,9 +578,9 @@ class NearestTransferFunction : public fn::MultiFunction { { if (use_mesh_) { const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); - const int domain_size = mesh.attribute_domain_size(domain_); + const int domain_num = mesh.attribute_domain_num(domain_); mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); + mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num); mesh_evaluator_->add(src_field_); mesh_evaluator_->evaluate(); mesh_data_ = &mesh_evaluator_->get_evaluated(0); @@ -588,9 +588,9 @@ class NearestTransferFunction : public fn::MultiFunction { if (use_points_) { const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); - const int domain_size = points.attribute_domain_size(domain_); + const int domain_num = points.attribute_domain_num(domain_); point_context_.emplace(GeometryComponentFieldContext(points, domain_)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size); + point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num); point_evaluator_->add(src_field_); point_evaluator_->evaluate(); point_data_ = &point_evaluator_->get_evaluated(0); @@ -658,9 +658,9 @@ class IndexTransferFunction : public fn::MultiFunction { if (component == nullptr) { return; } - const int domain_size = component->attribute_domain_size(domain_); + const int domain_num = component->attribute_domain_num(domain_); geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); - evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); evaluator_->add(src_field_); evaluator_->evaluate(); src_data_ = &evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index a5ca1cba28f..258c1ac3fba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -19,7 +19,7 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 992470e8279..e47dc22da04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -83,9 +83,9 @@ static void node_geo_exec(GeoNodeExecParams params) GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator{context, domain_size}; + FieldEvaluator evaluator{context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 378bac894e8..9a316190720 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -241,27 +241,27 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful case GEO_COMPONENT_TYPE_MESH: { const MeshComponent &mesh_component = *(const MeshComponent *)component; MeshInfo &info = this->mesh_info.emplace(); - info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE); break; } case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *(const CurveComponent *)component; CurveInfo &info = this->curve_info.emplace(); - info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE); break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; PointCloudInfo &info = this->pointcloud_info.emplace(); - info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT); break; } case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *(const InstancesComponent *)component; InstancesInfo &info = this->instances_info.emplace(); - info.tot_instances = instances_component.instances_amount(); + info.instances_num = instances_component.instances_num(); break; } case GEO_COMPONENT_TYPE_VOLUME: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index cea3084a418..39d8c453e43 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -119,14 +119,14 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); if (default_value == nullptr) { default_value = cpp_type->default_value(); } if (found_socket == nullptr) { - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } if (found_socket->type == SOCK_STRING) { @@ -140,40 +140,40 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, /* If the attribute doesn't exist, use the default value and output an error message * (except when the field is empty, to avoid spamming error messages, and not when * the domain is empty and we don't expect an attribute anyway). */ - if (!name.empty() && component.attribute_domain_size(domain) != 0) { + if (!name.empty() && component.attribute_domain_num(domain) != 0) { this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_INT) { const int value = this->get_input<int>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_RGBA) { const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized( CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } BLI_assert(false); - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 5d2e1663ae3..e8be093c606 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -226,6 +226,39 @@ void node_filter_label(const bNodeTree *UNUSED(ntree), const bNode *node, char * BLI_strncpy(label, IFACE_(name), maxlen); } +void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode) +{ + bNodeSocket *sock1 = (bNodeSocket *)sockets->first; + bNodeSocket *sock2 = sock1->next; + bNodeSocket *sock3 = sock2->next; + + node_sock_label_clear(sock1); + node_sock_label_clear(sock2); + node_sock_label_clear(sock3); + + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + node_sock_label(sock1, "Red"); + node_sock_label(sock2, "Green"); + node_sock_label(sock3, "Blue"); + break; + case NODE_COMBSEP_COLOR_HSL: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Lightness"); + break; + case NODE_COMBSEP_COLOR_HSV: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Value"); + break; + default: { + BLI_assert_unreachable(); + break; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/nodes/intern/node_util.h b/source/blender/nodes/intern/node_util.h index de03a176c0c..0a2a7a70091 100644 --- a/source/blender/nodes/intern/node_util.h +++ b/source/blender/nodes/intern/node_util.h @@ -65,6 +65,7 @@ void node_filter_label(const struct bNodeTree *ntree, const struct bNode *node, char *label, int maxlen); +void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode); /*** Link Handling */ diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 9b4ea0e0db6..3e90f185168 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC nodes/node_shader_rgb.cc nodes/node_shader_rgb_to_bw.cc nodes/node_shader_script.cc + nodes/node_shader_sepcomb_color.cc nodes/node_shader_sepcomb_hsv.cc nodes/node_shader_sepcomb_rgb.cc nodes/node_shader_sepcomb_xyz.cc diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index b1db0248d9f..eb47059063d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -28,48 +28,34 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - - CurveMapping *cumap = (CurveMapping *)node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[3][4]; - float range_xyz[3]; - - for (int a = 0; a < 3; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_xyz[a][0] = cm->mintable; - ext_xyz[a][2] = cm->maxtable; - range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) : - 1e8f; - ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) : - 1e8f; - } - else { - ext_xyz[a][1] = 0.0f; - ext_xyz[a][3] = 0.0f; - } - } + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); return GPU_stack_link(mat, node, - "curves_vec", + "curves_vector_mixed", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_xyz), - GPU_uniform(ext_xyz[0]), - GPU_uniform(ext_xyz[1]), - GPU_uniform(ext_xyz[2])); + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveVecFunction : public fn::MultiFunction { @@ -157,72 +143,59 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - bool use_opti = true; - - CurveMapping *cumap = (CurveMapping *)node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_rgba[4][4]; - float range_rgba[4]; - - for (int a = 0; a < CM_TOT; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_rgba[a][0] = cm->mintable; - ext_rgba[a][2] = cm->maxtable; - range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ? - (cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) : - 1e8f; - ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) : - 1e8f; - } - else { - ext_rgba[a][1] = 0.0f; - ext_rgba[a][3] = 0.0f; - } - - /* Check if rgb comps are just linear. */ - if (a < 3) { - if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f || - cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f || - cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) { - use_opti = false; - } - } - } - - if (use_opti) { + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + + /* Shader nodes don't do white balancing. */ + float black_level[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + float white_level[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + /* If the RGB curves do nothing, use a function that skips RGB computations. */ + if (BKE_curvemapping_is_map_identity(curve_mapping, 0) && + BKE_curvemapping_is_map_identity(curve_mapping, 1) && + BKE_curvemapping_is_map_identity(curve_mapping, 2)) { return GPU_stack_link(mat, node, - "curves_rgb_opti", + "curves_combined_only", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[3])); + GPU_constant(black_level), + GPU_constant(white_level), + band_texture, + GPU_constant(&band_layer), + GPU_uniform(&range_minimums[3]), + GPU_uniform(&range_dividers[3]), + GPU_uniform(&start_slopes[3]), + GPU_uniform(&end_slopes[3])); } return GPU_stack_link(mat, node, - "curves_rgb", + "curves_combined_rgb", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[0]), - GPU_uniform(ext_rgba[1]), - GPU_uniform(ext_rgba[2]), - GPU_uniform(ext_rgba[3])); + GPU_constant(black_level), + GPU_constant(white_level), + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveRGBFunction : public fn::MultiFunction { @@ -316,40 +289,34 @@ static int gpu_shader_curve_float(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - - CurveMapping *cumap = (CurveMapping *)node->storage; + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_F(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[4]; - float range_x; - - const CurveMap *cm = &cumap->cm[0]; - ext_xyz[0] = cm->mintable; - ext_xyz[2] = cm->maxtable; - range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f; - ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f; - } - else { - ext_xyz[1] = 0.0f; - ext_xyz[3] = 0.0f; - } return GPU_stack_link(mat, node, - "curve_float", + "curves_float_mixed", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(&range_x), - GPU_uniform(ext_xyz)); + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveFloatFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc new file mode 100644 index 00000000000..8e378ebabce --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static void node_combsep_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +/* **************** SEPARATE COLOR ******************** */ + +namespace blender::nodes::node_shader_separate_color_cc { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void sh_node_sepcolor_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); +} + +static void node_sepcolor_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + return "separate_color_rgb"; + case NODE_COMBSEP_COLOR_HSV: + return "separate_color_hsv"; + case NODE_COMBSEP_COLOR_HSL: + return "separate_color_hsl"; + } + + return nullptr; +} + +static int gpu_shader_sepcolor(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeCombSepColor &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.mode); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); + } + + return 0; +} + +} // namespace blender::nodes::node_shader_separate_color_cc + +void register_node_type_sh_sepcolor() +{ + namespace file_ns = blender::nodes::node_shader_separate_color_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_sepcolor_declare; + node_type_update(&ntype, file_ns::node_sepcolor_update); + node_type_init(&ntype, node_combsep_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_sepcolor); + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE COLOR ******************** */ + +namespace blender::nodes::node_shader_combine_color_cc { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void sh_node_combcolor_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); +} + +static void node_combcolor_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + return "combine_color_rgb"; + case NODE_COMBSEP_COLOR_HSV: + return "combine_color_hsv"; + case NODE_COMBSEP_COLOR_HSL: + return "combine_color_hsl"; + } + + return nullptr; +} + +static int gpu_shader_combcolor(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeCombSepColor &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.mode); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); + } + + return 0; +} + +} // namespace blender::nodes::node_shader_combine_color_cc + +void register_node_type_sh_combcolor() +{ + namespace file_ns = blender::nodes::node_shader_combine_color_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_combcolor_declare; + node_type_update(&ntype, file_ns::node_combcolor_update); + node_type_init(&ntype, node_combsep_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_combcolor); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc index 129c8cf4b97..6dfabe48292 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -36,7 +36,7 @@ void register_node_type_sh_sephsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_sephsv; node_type_gpu(&ntype, file_ns::gpu_shader_sephsv); @@ -72,7 +72,7 @@ void register_node_type_sh_combhsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_combhsv; node_type_gpu(&ntype, file_ns::gpu_shader_combhsv); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 657f591a50c..28b55047633 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -76,7 +76,7 @@ void register_node_type_sh_seprgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB_LEGACY, "Separate RGB", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_seprgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_seprgb); ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function; @@ -119,7 +119,7 @@ void register_node_type_sh_combrgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB_LEGACY, "Combine RGB", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_combrgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_combrgb); ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function; diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 7706f118507..5bed54ebfd7 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRC nodes/node_texture_at.c nodes/node_texture_bricks.c nodes/node_texture_checker.c + nodes/node_texture_combine_color.c nodes/node_texture_common.c nodes/node_texture_compose.c nodes/node_texture_coord.c @@ -39,6 +40,7 @@ set(SRC nodes/node_texture_proc.c nodes/node_texture_rotate.c nodes/node_texture_scale.c + nodes/node_texture_separate_color.c nodes/node_texture_texture.c nodes/node_texture_translate.c nodes/node_texture_valToNor.c diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index 76208104a8c..248114f242a 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -49,7 +49,7 @@ static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, sh } } -static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, short thread) +static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread) { TexDelegate *dg = in->data; if (dg) { @@ -59,7 +59,7 @@ static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, sho in->vec[1] = in->vec[2] = in->vec[0]; } } - memcpy(out, in->vec, sz * sizeof(float)); + memcpy(out, in->vec, num * sizeof(float)); } void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread) diff --git a/source/blender/nodes/texture/nodes/node_texture_combine_color.c b/source/blender/nodes/texture/nodes/node_texture_combine_color.c new file mode 100644 index 00000000000..459553bc950 --- /dev/null +++ b/source/blender/nodes/texture/nodes/node_texture_combine_color.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup texnodes + */ + +#include "BLI_listbase.h" +#include "NOD_texture.h" +#include "node_texture_util.h" + +static bNodeSocketTemplate inputs[] = { + {SOCK_FLOAT, N_("Red"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Green"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Blue"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {-1, ""}, +}; +static bNodeSocketTemplate outputs[] = { + {SOCK_RGBA, N_("Color")}, + {-1, ""}, +}; + +static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + int i; + for (i = 0; i < 4; i++) { + out[i] = tex_input_value(in[i], p, thread); + } + /* Apply color space if required. */ + switch (node->custom1) { + case NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case NODE_COMBSEP_COLOR_HSV: { + hsv_to_rgb_v(out, out); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + hsl_to_rgb_v(out, out); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +static void update(bNodeTree *UNUSED(ntree), bNode *node) +{ + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)node->custom1); +} + +static void exec(void *data, + int UNUSED(thread), + bNode *node, + bNodeExecData *execdata, + bNodeStack **in, + bNodeStack **out) +{ + tex_output(node, execdata, in, out[0], &colorfn, data); +} + +void register_node_type_tex_combine_color(void) +{ + static bNodeType ntype; + + tex_node_type_base(&ntype, TEX_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_OP_COLOR); + node_type_socket_templates(&ntype, inputs, outputs); + node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c index e341b65ac97..ef14062c72d 100644 --- a/source/blender/nodes/texture/nodes/node_texture_compose.c +++ b/source/blender/nodes/texture/nodes/node_texture_compose.c @@ -42,7 +42,7 @@ void register_node_type_tex_compose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base(&ntype, TEX_NODE_COMPOSE_LEGACY, "Combine RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_decompose.c b/source/blender/nodes/texture/nodes/node_texture_decompose.c index 21c3944e255..2d42fa4602e 100644 --- a/source/blender/nodes/texture/nodes/node_texture_decompose.c +++ b/source/blender/nodes/texture/nodes/node_texture_decompose.c @@ -62,7 +62,7 @@ void register_node_type_tex_decompose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE_LEGACY, "Separate RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_separate_color.c b/source/blender/nodes/texture/nodes/node_texture_separate_color.c new file mode 100644 index 00000000000..a482a3f3421 --- /dev/null +++ b/source/blender/nodes/texture/nodes/node_texture_separate_color.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup texnodes + */ + +#include "BLI_listbase.h" +#include "NOD_texture.h" +#include "node_texture_util.h" +#include <math.h> + +static bNodeSocketTemplate inputs[] = { + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, + {-1, ""}, +}; +static bNodeSocketTemplate outputs[] = { + {SOCK_FLOAT, N_("Red")}, + {SOCK_FLOAT, N_("Green")}, + {SOCK_FLOAT, N_("Blue")}, + {SOCK_FLOAT, N_("Alpha")}, + {-1, ""}, +}; + +static void apply_color_space(float *out, NodeCombSepColorMode type) +{ + switch (type) { + case NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case NODE_COMBSEP_COLOR_HSV: { + rgb_to_hsv_v(out, out); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + rgb_to_hsl_v(out, out); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +static void valuefn_r(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[0]; +} + +static void valuefn_g(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[1]; +} + +static void valuefn_b(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[2]; +} + +static void valuefn_a(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + *out = out[3]; +} + +static void update(bNodeTree *UNUSED(ntree), bNode *node) +{ + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)node->custom1); +} + +static void exec(void *data, + int UNUSED(thread), + bNode *node, + bNodeExecData *execdata, + bNodeStack **in, + bNodeStack **out) +{ + tex_output(node, execdata, in, out[0], &valuefn_r, data); + tex_output(node, execdata, in, out[1], &valuefn_g, data); + tex_output(node, execdata, in, out[2], &valuefn_b, data); + tex_output(node, execdata, in, out[3], &valuefn_a, data); +} + +void register_node_type_tex_separate_color(void) +{ + static bNodeType ntype; + + tex_node_type_base(&ntype, TEX_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_OP_COLOR); + node_type_socket_templates(&ntype, inputs, outputs); + node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index baa2b0deb71..972a782d650 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1259,10 +1259,16 @@ static PyObject *bpy_bmesh_select_flush(BPy_BMesh *self, PyObject *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpy_bmesh_normal_update_doc, - ".. method:: normal_update()\n" - "\n" - " Update mesh normals.\n"); +PyDoc_STRVAR( + bpy_bmesh_normal_update_doc, + ".. method:: normal_update()\n" + "\n" + " Update normals of mesh faces and verts.\n" + "\n" + " .. note::\n" + "\n" + " The normal of any vertex where :attr:`is_wire` is True will be a zero vector.\n"); + static PyObject *bpy_bmesh_normal_update(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); @@ -1611,7 +1617,12 @@ static PyObject *bpy_bmvert_calc_shell_factor(BPy_BMVert *self) PyDoc_STRVAR(bpy_bmvert_normal_update_doc, ".. method:: normal_update()\n" "\n" - " Update vertex normal.\n"); + " Update vertex normal.\n" + " This does not update the normals of adjoining faces.\n" + "\n" + " .. note::\n" + "\n" + " The vertex normal will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmvert_normal_update(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); @@ -1773,10 +1784,15 @@ static PyObject *bpy_bmedge_other_vert(BPy_BMEdge *self, BPy_BMVert *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpy_bmedge_normal_update_doc, - ".. method:: normal_update()\n" - "\n" - " Update edges vertex normals.\n"); +PyDoc_STRVAR( + bpy_bmedge_normal_update_doc, + ".. method:: normal_update()\n" + "\n" + " Update normals of all connected faces and the edge verts.\n" + "\n" + " .. note::\n" + "\n" + " The normal of edge vertex will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmedge_normal_update(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); @@ -2012,7 +2028,8 @@ static PyObject *bpy_bmface_calc_center_bounds(BPy_BMFace *self) PyDoc_STRVAR(bpy_bmface_normal_update_doc, ".. method:: normal_update()\n" "\n" - " Update face's normal.\n"); + " Update face normal based on the positions of the face verts.\n" + " This does not update the normals of face verts.\n"); static PyObject *bpy_bmface_normal_update(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 9e45105d105..060b7758ea9 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -50,7 +50,7 @@ static PyObject *py_blf_position(PyObject *UNUSED(self), PyObject *args) PyDoc_STRVAR(py_blf_size_doc, ".. function:: size(fontid, size, dpi)\n" "\n" - " Set the size and dpi for drawing text.\n" + " Set the size and DPI for drawing text.\n" "\n" " :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default " "font use 0.\n" diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index ef11d1ab32d..e6d90c46866 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -559,7 +559,11 @@ static PyMethodDef IMB_methods[] = { {NULL, NULL, 0, NULL}, }; -PyDoc_STRVAR(IMB_doc, "This module provides access to Blender's image manipulation API."); +PyDoc_STRVAR(IMB_doc, + "This module provides access to Blender's image manipulation API.\n" + "\n" + "It provides access to image buffers outside of Blender's\n" + ":class:`bpy.types.Image` data-block context.\n"); static struct PyModuleDef IMB_module_def = { PyModuleDef_HEAD_INIT, "imbuf", /* m_name */ @@ -596,7 +600,13 @@ PyObject *BPyInit_imbuf(void) * for docs and the ability to use with built-ins such as `isinstance`, `issubclass`. * \{ */ -PyDoc_STRVAR(IMB_types_doc, "This module provides access to image buffer types."); +PyDoc_STRVAR(IMB_types_doc, + "This module provides access to image buffer types.\n" + "\n" + ".. note::\n" + "\n" + " Image buffer is also the structure used by :class:`bpy.types.Image`\n" + " ID type to store and manipulate image data at runtime.\n"); static struct PyModuleDef IMB_types_module_def = { PyModuleDef_HEAD_INIT, diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index e726cb7883d..8ccb29beb13 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -29,8 +29,8 @@ set(SRC gpu_py_offscreen.c gpu_py_platform.c gpu_py_select.c - gpu_py_shader_create_info.cc gpu_py_shader.c + gpu_py_shader_create_info.cc gpu_py_state.c gpu_py_texture.c gpu_py_types.c diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 77333c6dfea..80c48e31510 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -46,6 +46,9 @@ "``3D_FLAT_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ + "``3D_IMAGE``\n" \ + " :Attributes: vec3 pos, vec2 texCoord\n" \ + " :Uniforms: sampler2D image\n" \ "``3D_SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ @@ -68,6 +71,7 @@ static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, + {GPU_SHADER_3D_IMAGE, "3D_IMAGE"}, {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index 3e0698caa50..1e5856a3285 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -35,7 +35,7 @@ static const char *bpyunits_usystem_items[] = { NULL, }; -static const char *bpyunits_ucategorie_items[] = { +static const char *bpyunits_ucategories_items[] = { "NONE", "LENGTH", "AREA", @@ -43,20 +43,26 @@ static const char *bpyunits_ucategorie_items[] = { "MASS", "ROTATION", "TIME", + "TIME_ABSOLUTE", "VELOCITY", "ACCELERATION", "CAMERA", "POWER", + "TEMPERATURE", NULL, }; +BLI_STATIC_ASSERT( + ARRAY_SIZE(bpyunits_ucategories_items) == B_UNIT_TYPE_TOT + 1, + "`bpyunits_ucategories_items` should match `B_UNIT_` enum items in `BKE_units.h`") + /** * These fields are just empty placeholders, actual values get set in initializations functions. * This allows us to avoid many handwriting, and above all, * to keep all systems/categories definition stuff in `BKE_unit.h`. */ static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usystem_items)]; -static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)]; +static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategories_items)]; static PyStructSequence_Desc bpyunits_systems_desc = { "bpy.utils.units.systems", /* name */ @@ -114,7 +120,7 @@ static bool bpyunits_validate(const char *usys_str, const char *ucat_str, int *r return false; } - *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategorie_items); + *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategories_items); if (*r_ucat < 0) { PyErr_Format(PyExc_ValueError, "Unknown unit category specified: %.200s.", ucat_str); return false; @@ -356,7 +362,7 @@ PyObject *BPY_utils_units(void) /* bpy.utils.units.categories */ item = py_structseq_from_strings( - &BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategorie_items); + &BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategories_items); PyModule_AddObject(submodule, "categories", item); /* steals ref */ return submodule; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 5953c0f0f8f..bf876163013 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -330,10 +330,10 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, { int i; int hit_mesh = -1; - float hit_distance = max_ray_distance; - if (hit_distance == 0.0f) { + float hit_distance_squared = max_ray_distance * max_ray_distance; + if (hit_distance_squared == 0.0f) { /* No ray distance set, use maximum. */ - hit_distance = FLT_MAX; + hit_distance_squared = FLT_MAX; } BVHTreeRayHit *hits; @@ -365,16 +365,14 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, } if (hits[i].index != -1) { - float distance; - float hit_world[3]; - /* distance comparison in world space */ + float hit_world[3]; mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); - distance = len_squared_v3v3(hit_world, co); + float distance_squared = len_squared_v3v3(hit_world, co); - if (distance < hit_distance) { + if (distance_squared < hit_distance_squared) { hit_mesh = i; - hit_distance = distance; + hit_distance_squared = distance_squared; } } } diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index f93397eedab..dc9ad2ddb5e 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -464,140 +464,141 @@ static void do_multires_bake(MultiresBakeRender *bkr, const MLoopTri *mlooptri = dm->getLoopTriArray(dm); const int lvl = bkr->lvl; int tot_tri = dm->getNumLoopTri(dm); + if (tot_tri < 1) { + return; + } - if (tot_tri > 0) { - MultiresBakeThread *handles; - MultiresBakeQueue queue; - - MVert *mvert = dm->getVertArray(dm); - MPoly *mpoly = dm->getPolyArray(dm); - MLoop *mloop = dm->getLoopArray(dm); - MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - float *pvtangent = NULL; - - ListBase threads; - int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); - - void *bake_data = NULL; - - Mesh *temp_mesh = BKE_mesh_new_nomain( - dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); - memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); - memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); - memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); - memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); - const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); - - if (require_tangent) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - BKE_mesh_calc_loop_tangent_ex( - dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - true, - NULL, - 0, - vert_normals, - poly_normals, - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), - (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); - } - - pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + MultiresBakeThread *handles; + MultiresBakeQueue queue; + + MVert *mvert = dm->getVertArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + MLoop *mloop = dm->getLoopArray(dm); + MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); + float *pvtangent = NULL; + + ListBase threads; + int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); + + void *bake_data = NULL; + + Mesh *temp_mesh = BKE_mesh_new_nomain( + dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); + memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); + memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); + memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); + memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); + + if (require_tangent) { + if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { + BKE_mesh_calc_loop_tangent_ex( + dm->getVertArray(dm), + dm->getPolyArray(dm), + dm->getNumPolys(dm), + dm->getLoopArray(dm), + dm->getLoopTriArray(dm), + dm->getNumLoopTri(dm), + &dm->loopData, + true, + NULL, + 0, + vert_normals, + poly_normals, + (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ + /* result */ + &dm->loopData, + dm->getNumLoops(dm), + &dm->tangent_mask); } - /* all threads shares the same custom bake data */ - if (initBakeData) { - bake_data = initBakeData(bkr, ibuf); - } + pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + } - if (tot_thread > 1) { - BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); - } + /* all threads shares the same custom bake data */ + if (initBakeData) { + bake_data = initBakeData(bkr, ibuf); + } - handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); - - init_ccgdm_arrays(bkr->hires_dm); - - /* faces queue */ - queue.cur_tri = 0; - queue.tot_tri = tot_tri; - BLI_spin_init(&queue.spin); - - /* fill in threads handles */ - for (i = 0; i < tot_thread; i++) { - MultiresBakeThread *handle = &handles[i]; - - handle->bkr = bkr; - handle->image = ima; - handle->queue = &queue; - - handle->data.mpoly = mpoly; - handle->data.mvert = mvert; - handle->data.vert_normals = vert_normals; - handle->data.mloopuv = mloopuv; - BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); - handle->data.mlooptri = mlooptri; - handle->data.mloop = mloop; - handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ - handle->data.w = ibuf->x; - handle->data.h = ibuf->y; - handle->data.lores_dm = dm; - handle->data.hires_dm = bkr->hires_dm; - handle->data.lvl = lvl; - handle->data.pass_data = passKnownData; - handle->data.thread_data = handle; - handle->data.bake_data = bake_data; - handle->data.ibuf = ibuf; - - handle->height_min = FLT_MAX; - handle->height_max = -FLT_MAX; - - init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - - if (tot_thread > 1) { - BLI_threadpool_insert(&threads, handle); - } - } + if (tot_thread > 1) { + BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); + } + + handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); + + init_ccgdm_arrays(bkr->hires_dm); + + /* faces queue */ + queue.cur_tri = 0; + queue.tot_tri = tot_tri; + BLI_spin_init(&queue.spin); + + /* fill in threads handles */ + for (i = 0; i < tot_thread; i++) { + MultiresBakeThread *handle = &handles[i]; + + handle->bkr = bkr; + handle->image = ima; + handle->queue = &queue; + + handle->data.mpoly = mpoly; + handle->data.mvert = mvert; + handle->data.vert_normals = vert_normals; + handle->data.mloopuv = mloopuv; + BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); + handle->data.mlooptri = mlooptri; + handle->data.mloop = mloop; + handle->data.pvtangent = pvtangent; + handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ + handle->data.w = ibuf->x; + handle->data.h = ibuf->y; + handle->data.lores_dm = dm; + handle->data.hires_dm = bkr->hires_dm; + handle->data.lvl = lvl; + handle->data.pass_data = passKnownData; + handle->data.thread_data = handle; + handle->data.bake_data = bake_data; + handle->data.ibuf = ibuf; + + handle->height_min = FLT_MAX; + handle->height_max = -FLT_MAX; + + init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - /* run threads */ if (tot_thread > 1) { - BLI_threadpool_end(&threads); + BLI_threadpool_insert(&threads, handle); } - else { - do_multires_bake_thread(&handles[0]); - } - - /* construct bake result */ - result->height_min = handles[0].height_min; - result->height_max = handles[0].height_max; + } - for (i = 1; i < tot_thread; i++) { - result->height_min = min_ff(result->height_min, handles[i].height_min); - result->height_max = max_ff(result->height_max, handles[i].height_max); - } + /* run threads */ + if (tot_thread > 1) { + BLI_threadpool_end(&threads); + } + else { + do_multires_bake_thread(&handles[0]); + } - BLI_spin_end(&queue.spin); + /* construct bake result */ + result->height_min = handles[0].height_min; + result->height_max = handles[0].height_max; - /* finalize baking */ - if (freeBakeData) { - freeBakeData(bake_data); - } + for (i = 1; i < tot_thread; i++) { + result->height_min = min_ff(result->height_min, handles[i].height_min); + result->height_max = max_ff(result->height_max, handles[i].height_max); + } - MEM_freeN(handles); + BLI_spin_end(&queue.spin); - BKE_id_free(NULL, temp_mesh); + /* finalize baking */ + if (freeBakeData) { + freeBakeData(bake_data); } + + MEM_freeN(handles); + + BKE_id_free(NULL, temp_mesh); } /* mode = 0: interpolate normals, diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index a9e1495d9bf..96094e9e7ef 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -28,6 +28,11 @@ struct wmOperatorType; #define WM_TOOLSYSTEM_SPACE_MASK \ ((1 << SPACE_IMAGE) | (1 << SPACE_NODE) | (1 << SPACE_VIEW3D) | (1 << SPACE_SEQ)) +/** + * Space-types that define their own "mode" (as returned by #WM_toolsystem_mode_from_spacetype). + */ +#define WM_TOOLSYSTEM_SPACE_MASK_MODE_FROM_SPACE ((1 << SPACE_IMAGE) | (1 << SPACE_SEQ)) + /* Values that define a category of active tool. */ typedef struct bToolKey { int space_type; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 3ee5c85c031..9e9f195c430 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -812,6 +812,10 @@ typedef struct wmXrActionData { char action_set[64]; /** Action name. */ char action[64]; + /** User path. E.g. "/user/hand/left" */ + char user_path[64]; + /** Other user path, for bimanual actions. E.g. "/user/hand/right" */ + char user_path_other[64]; /** Type. */ eXrActionType type; /** State. Set appropriately based on type. */ diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 02da798495b..d2ade7b0376 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -467,7 +467,7 @@ static bool wm_draw_region_bind(bContext *C, ARegion *region, int view) } if (region->draw_buffer->viewport) { - if (G.is_rendering && C != NULL) { + if (G.is_rendering && C != NULL && U.experimental.use_draw_manager_acquire_lock) { Scene *scene = CTX_data_scene(C); RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine); if (RE_engine_is_opengl(render_engine_type)) { diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 1c3f7ed3e7a..58d7a55eddc 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -101,6 +101,7 @@ static int wm_operator_call_internal(bContext *C, static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win); +static void wm_operator_free_for_fileselect(wmOperator *file_operator); /* -------------------------------------------------------------------- */ /** \name Event Management @@ -1904,8 +1905,15 @@ void wm_event_free_handler(wmEventHandler *handler) MEM_freeN(handler); } -/* Only set context when area/region is part of screen. */ -static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event) +/** + * Check if the handler's area and/or region are actually part of the screen, and return them if + * so. + */ +static void wm_handler_op_context_get_if_valid(bContext *C, + wmEventHandler_Op *handler, + const wmEvent *event, + ScrArea **r_area, + ARegion **r_region) { wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C); /* It's probably fine to always use #WM_window_get_active_screen() to get the screen. But this @@ -1913,12 +1921,15 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const * possible. */ bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C); + *r_area = NULL; + *r_region = NULL; + if (screen == NULL || handler->op == NULL) { return; } if (handler->context.area == NULL) { - CTX_wm_area_set(C, NULL); + /* Pass */ } else { ScrArea *area = NULL; @@ -1942,7 +1953,7 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const else { ARegion *region; wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL; - CTX_wm_area_set(C, area); + *r_area = area; if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) { region = BKE_area_find_region_xy(area, handler->context.region_type, event->xy); @@ -1965,12 +1976,21 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const /* No warning print here, after full-area and back regions are remade. */ if (region) { - CTX_wm_region_set(C, region); + *r_region = region; } } } } +static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event) +{ + ScrArea *area = NULL; + ARegion *region = NULL; + wm_handler_op_context_get_if_valid(C, handler, event, &area, ®ion); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); +} + void WM_event_remove_handlers(bContext *C, ListBase *handlers) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2017,7 +2037,13 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) } WM_cursor_grab_disable(win, NULL); - WM_operator_free(handler->op); + + if (handler->is_fileselect) { + wm_operator_free_for_fileselect(handler->op); + } + else { + WM_operator_free(handler->op); + } } } else if (handler_base->type == WM_HANDLER_TYPE_UI) { @@ -2454,6 +2480,22 @@ static int wm_handler_operator_call(bContext *C, return WM_HANDLER_BREAK; } +static void wm_operator_free_for_fileselect(wmOperator *file_operator) +{ + LISTBASE_FOREACH (bScreen *, screen, &G_MAIN->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_FILE) { + SpaceFile *sfile = area->spacedata.first; + if (sfile->op == file_operator) { + sfile->op = NULL; + } + } + } + } + + WM_operator_free(file_operator); +} + /** * File-select handlers are only in the window queue, * so it's safe to switch screens or area types. @@ -2507,6 +2549,9 @@ static int wm_handler_fileselect_do(bContext *C, case EVT_FILESELECT_CANCEL: case EVT_FILESELECT_EXTERNAL_CANCEL: { wmWindow *ctx_win = CTX_wm_window(C); + wmEvent *eventstate = ctx_win->eventstate; + /* The root window of the operation as determined in #WM_event_add_fileselect(). */ + wmWindow *root_win = handler->context.win; /* Remove link now, for load file case before removing. */ BLI_remlink(handlers, handler); @@ -2542,21 +2587,17 @@ static int wm_handler_fileselect_do(bContext *C, ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized); if (BLI_listbase_is_single(&file_area->spacedata)) { - BLI_assert(ctx_win != win); + BLI_assert(root_win != win); wm_window_close(C, wm, win); - CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */ + CTX_wm_window_set(C, root_win); /* #wm_window_close() NULLs. */ /* Some operators expect a drawable context (for #EVT_FILESELECT_EXEC). */ - wm_window_make_drawable(wm, ctx_win); + wm_window_make_drawable(wm, root_win); /* Ensure correct cursor position, otherwise, popups may close immediately after * opening (#UI_BLOCK_MOVEMOUSE_QUIT). */ - wm_cursor_position_get( - ctx_win, &ctx_win->eventstate->xy[0], &ctx_win->eventstate->xy[1]); - wm->winactive = ctx_win; /* Reports use this... */ - if (handler->context.win == win) { - handler->context.win = NULL; - } + wm_cursor_position_get(root_win, &eventstate->xy[0], &eventstate->xy[1]); + wm->winactive = root_win; /* Reports use this... */ } else if (file_area->full) { ED_screen_full_prevspace(C, file_area); @@ -2575,7 +2616,13 @@ static int wm_handler_fileselect_do(bContext *C, } } - wm_handler_op_context(C, handler, ctx_win->eventstate); + CTX_wm_window_set(C, root_win); + wm_handler_op_context(C, handler, eventstate); + /* At this point context is supposed to match the root context determined by + * #WM_event_add_fileselect(). */ + BLI_assert(!CTX_wm_area(C) || (CTX_wm_area(C) == handler->context.area)); + BLI_assert(!CTX_wm_region(C) || (CTX_wm_region(C) == handler->context.region)); + ScrArea *handler_area = CTX_wm_area(C); /* Make sure new context area is ready, the operator callback may operate on it. */ if (handler_area) { @@ -2644,7 +2691,7 @@ static int wm_handler_fileselect_do(bContext *C, } if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { - WM_operator_free(handler->op); + wm_operator_free_for_fileselect(handler->op); } } else { @@ -2659,8 +2706,7 @@ static int wm_handler_fileselect_do(bContext *C, wm->op_undo_depth--; } } - - WM_operator_free(handler->op); + wm_operator_free_for_fileselect(handler->op); } CTX_wm_area_set(C, NULL); @@ -3981,58 +4027,118 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval event.type = EVT_FILESELECT; event.val = eventval; + event.flag = 0; event.customdata = ophandle; /* Only as void pointer type check. */ wm_event_add(win, &event); } } +/** + * From the context window, try to find a window that is appropriate for use as root window of a + * modal File Browser (modal means: there is a #SpaceFile.op to execute). The root window will + * become the parent of the File Browser and provides a context to execute the file operator in, + * even after closing the File Browser. + * + * An appropriate window is either of the following: + * * A parent window that does not yet contain a modal File Browser. This is determined using + * #ED_fileselect_handler_area_find_any_with_op(). + * * A parent window containing a modal File Browser, but in a maximized/fullscreen state. Users + * shouldn't be able to put a temporary screen like the modal File Browser into + * maximized/fullscreen state themselves. So this setup indicates that the File Browser was + * opened using #USER_TEMP_SPACE_DISPLAY_FULLSCREEN. + * + * If no appropriate parent window can be found from the context window, return the first + * registered window (which can be assumed to be a regular window, e.g. no modal File Browser; this + * is asserted). + */ +static wmWindow *wm_event_find_fileselect_root_window_from_context(const bContext *C) +{ + wmWindow *ctx_win = CTX_wm_window(C); + + for (wmWindow *ctx_win_or_parent = ctx_win; ctx_win_or_parent; + ctx_win_or_parent = ctx_win_or_parent->parent) { + ScrArea *file_area = ED_fileselect_handler_area_find_any_with_op(ctx_win_or_parent); + + if (!file_area) { + return ctx_win_or_parent; + } + + if (file_area->full) { + return ctx_win_or_parent; + } + } + + /* Fallback to the first window. */ + const wmWindowManager *wm = CTX_wm_manager(C); + BLI_assert(!ED_fileselect_handler_area_find_any_with_op(wm->windows.first)); + return wm->windows.first; +} + /* Operator is supposed to have a filled "path" property. */ /* Optional property: file-type (XXX enum?) */ void WM_event_add_fileselect(bContext *C, wmOperator *op) { wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - const bool is_temp_screen = WM_window_is_temp_screen(win); + wmWindow *ctx_win = CTX_wm_window(C); + + /* The following vars define the root context. That is essentially the "parent" context of the + * File Browser operation, to be restored for eventually executing the file operation. */ + wmWindow *root_win = wm_event_find_fileselect_root_window_from_context(C); + /* Determined later. */ + ScrArea *root_area = NULL; + ARegion *root_region = NULL; /* Close any popups, like when opening a file browser from the splash. */ - UI_popup_handlers_remove_all(C, &win->modalhandlers); + UI_popup_handlers_remove_all(C, &root_win->modalhandlers); - if (!is_temp_screen) { - /* Only allow 1 file selector open per window. */ - LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &win->modalhandlers) { - if (handler_base->type == WM_HANDLER_TYPE_OP) { - wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; - if (handler->is_fileselect == false) { - continue; - } + CTX_wm_window_set(C, root_win); - ScrArea *file_area = ED_fileselect_handler_area_find(win, handler->op); + /* The root window may already have a File Browser open. Cancel it if so, only 1 should be open + * per window. The root context of this operation is also used for the new operation. */ + LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &root_win->modalhandlers) { + if (handler_base->type == WM_HANDLER_TYPE_OP) { + wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; + if (handler->is_fileselect == false) { + continue; + } - if (file_area) { - CTX_wm_area_set(C, file_area); - wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL); - } - /* If not found we stop the handler without changing the screen. */ - else { - wm_handler_fileselect_do( - C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); - } + wm_handler_op_context_get_if_valid( + C, handler, ctx_win->eventstate, &root_area, &root_region); + + ScrArea *file_area = ED_fileselect_handler_area_find(root_win, handler->op); + + if (file_area) { + CTX_wm_area_set(C, file_area); + wm_handler_fileselect_do(C, &root_win->modalhandlers, handler, EVT_FILESELECT_CANCEL); + } + /* If not found we stop the handler without changing the screen. */ + else { + wm_handler_fileselect_do( + C, &root_win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); } } } + BLI_assert(root_win != NULL); + /* When not reusing the root context from a previous file browsing operation, use the current + * area & region, if they are inside the root window. */ + if (!root_area && ctx_win == root_win) { + root_area = CTX_wm_area(C); + root_region = CTX_wm_region(C); + } + wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__); handler->head.type = WM_HANDLER_TYPE_OP; handler->is_fileselect = true; handler->op = op; - handler->context.win = CTX_wm_window(C); - handler->context.area = CTX_wm_area(C); - handler->context.region = CTX_wm_region(C); + handler->context.win = root_win; + handler->context.area = root_area; + handler->context.region = root_region; - BLI_addhead(&win->modalhandlers, handler); + BLI_addhead(&root_win->modalhandlers, handler); /* Check props once before invoking if check is available * ensures initial properties are valid. */ @@ -4041,6 +4147,8 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) } WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN); + + CTX_wm_window_set(C, ctx_win); } /** \} */ @@ -5727,6 +5835,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wmEvent test_event = *win->eventstate; test_event.type = event_data[data_index].event_type; test_event.val = event_data[data_index].event_value; + test_event.flag = 0; wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 388abe21578..984a8ef41d0 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -492,6 +492,14 @@ bool WM_toolsystem_key_from_context(ViewLayer *view_layer, ScrArea *area, bToolK void WM_toolsystem_refresh_active(bContext *C) { Main *bmain = CTX_data_main(C); + + struct { + wmWindow *win; + ScrArea *area; + ARegion *region; + bool is_set; + } context_prev = {0}; + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { WorkSpace *workspace = WM_window_get_active_workspace(win); @@ -511,6 +519,16 @@ void WM_toolsystem_refresh_active(bContext *C) }; bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); if (tref != area->runtime.tool) { + if (context_prev.is_set == false) { + context_prev.win = CTX_wm_window(C); + context_prev.area = CTX_wm_area(C); + context_prev.region = CTX_wm_region(C); + context_prev.is_set = true; + } + + CTX_wm_window_set(C, win); + CTX_wm_area_set(C, area); + toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL); } } @@ -518,6 +536,12 @@ void WM_toolsystem_refresh_active(bContext *C) } } + if (context_prev.is_set) { + CTX_wm_window_set(C, context_prev.win); + CTX_wm_area_set(C, context_prev.area); + CTX_wm_region_set(C, context_prev.region); + } + BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT); LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { @@ -571,6 +595,7 @@ void WM_toolsystem_refresh_screen_all(Main *bmain) /* Update all ScrArea's tools */ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + WM_toolsystem_refresh_screen_window(win); } } } @@ -607,6 +632,17 @@ bToolRef *WM_toolsystem_ref_set_by_id_ex( if (ot == NULL) { return NULL; } + + /* Some contexts use the current space type (image editor for e.g.), + * ensure this is set correctly or there is no area. */ +#ifndef NDEBUG + /* Exclude this check for some space types where the space type isn't used. */ + if ((1 << tkey->space_type) & WM_TOOLSYSTEM_SPACE_MASK_MODE_FROM_SPACE) { + ScrArea *area = CTX_wm_area(C); + BLI_assert(area == NULL || area->spacetype == tkey->space_type); + } +#endif + PointerRNA op_props; WM_operator_properties_create_ptr(&op_props, ot); RNA_string_set(&op_props, "name", name); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 382a37e09e5..c0427f9be9a 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -578,6 +578,9 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, wm_window_swap_buffers(win); + /* Clear double buffer to avoids flickering of new windows on certain drivers. (See T97600) */ + GPU_clear_color(0.55f, 0.55f, 0.55f, 1.0f); + // GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified); } else { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 0a76fd0a25f..2a829e274d9 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1025,6 +1025,10 @@ static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__); strcpy(data->action_set, action_set_name); strcpy(data->action, action->name); + strcpy(data->user_path, action->subaction_paths[subaction_idx]); + if (bimanual) { + strcpy(data->user_path_other, action->subaction_paths[subaction_idx_other]); + } data->type = action->type; switch (action->type) { |