diff options
302 files changed, 6990 insertions, 1815 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/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index 92dea453133..80c53d05c28 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## Update and uncomment this in the release branch -set(BLENDER_VERSION 3.2) +# set(BLENDER_VERSION 3.1) function(download_source dep) set(TARGET_FILE ${${dep}_FILE}) 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/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index d1f1fe78d21..8222f2ff0b9 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -5,38 +5,38 @@ update-code: git: submodules: - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: release/scripts/addons - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: release/scripts/addons_contrib - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: release/datafiles/locale - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: source/tools svn: libraries: darwin-arm64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/darwin_arm64 darwin-x86_64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/darwin linux-x86_64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/linux_centos7_x86_64 windows-amd64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/win64_vc15 tests: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/tests benchmarks: diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index b8f54f548b4..0176a888377 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.2 +PROJECT_NUMBER = V3.3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a 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/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/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/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index d97854a52d0..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 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/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 08352a6231f..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" @@ -508,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/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/icons/ops.curves.sculpt_puff.dat b/release/datafiles/icons/ops.curves.sculpt_puff.dat Binary files differnew file mode 100644 index 00000000000..db2bab46bfe --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_puff.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 44cf4b4bf74f22fff55941e39cebeacec68a5a8 +Subproject 2e715d54178e24ea463c7bc0cdeb27c682c39ec diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex 332ee39849e..eb1250cf5a5 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png 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/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index b3f0c055769..4c6e2508859 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -1145,3 +1145,11 @@ class TextureNode(NodeInternal): @classmethod def poll(cls, ntree): return ntree.bl_idname == 'TextureNodeTree' + + +class GeometryNode(NodeInternal): + __slots__ = () + + @classmethod + def poll(cls, ntree): + return ntree.bl_idname == 'GeometryNodeTree' diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 78620c41d1e..99563c83bf9 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), ]) @@ -3977,6 +3981,8 @@ def km_grease_pencil_stroke_sculpt_mode(params): ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Active material + op_menu("GPENCIL_MT_material_active", {"type": 'U', "value": 'PRESS'}), # Merge Layer ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Keyframe menu diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 481753d5e79..189210d8540 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -41,17 +41,7 @@ class AnnotationDrawingToolsPanel: row.prop_enum(tool_settings, "annotation_stroke_placement_view2d", 'IMAGE', text="Image") -class GreasePencilSculptOptionsPanel: - bl_label = "Sculpt Strokes" - - @classmethod - def poll(cls, context): - tool_settings = context.scene.tool_settings - settings = tool_settings.gpencil_sculpt_paint - brush = settings.brush - tool = brush.gpencil_sculpt_tool - - return bool(tool in {'SMOOTH', 'RANDOMIZE'}) +class GreasePencilSculptAdvancedPanel: def draw(self, context): layout = self.layout @@ -59,17 +49,21 @@ class GreasePencilSculptOptionsPanel: layout.use_property_decorate = False tool_settings = context.scene.tool_settings - settings = tool_settings.gpencil_sculpt_paint - brush = settings.brush - gp_settings = brush.gpencil_settings + brush = context.tool_settings.gpencil_sculpt_paint.brush tool = brush.gpencil_sculpt_tool + gp_settings = brush.gpencil_settings - if tool in {'SMOOTH', 'RANDOMIZE'}: - layout.prop(gp_settings, "use_edit_position", text="Affect Position") - layout.prop(gp_settings, "use_edit_strength", text="Affect Strength") - layout.prop(gp_settings, "use_edit_thickness", text="Affect Thickness") + col = layout.column(heading="Auto-Masking", align=True) + col.prop(gp_settings, "use_automasking_stroke", text="Stroke") + col.prop(gp_settings, "use_automasking_layer", text="Layer") + col.prop(gp_settings, "use_automasking_material", text="Material") - layout.prop(gp_settings, "use_edit_uv", text="Affect UV") + if tool in {'SMOOTH', 'RANDOMIZE'}: + col = layout.column(heading="Affect", align=True) + col.prop(gp_settings, "use_edit_position", text="Position") + col.prop(gp_settings, "use_edit_strength", text="Strength") + col.prop(gp_settings, "use_edit_thickness", text="Thickness") + col.prop(gp_settings, "use_edit_uv", text="UV") # GP Object Tool Settings diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9a116aa1717..782fec91f91 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -769,6 +769,18 @@ 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") + 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 +839,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 +938,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_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 e0abb3a8d89..6654053bef4 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2274,6 +2274,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_new_curves_type"}, "T68981"), + ({"property": "use_new_curves_tools"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), ({"property": "use_full_frame_compositor"}, "T88150"), ({"property": "enable_eevee_next"}, "T93220"), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 3b966a28d33..60a28fb1ebf 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -109,8 +109,8 @@ class VIEW3D_HT_tool_header(Header): if is_valid_context: brush = context.tool_settings.gpencil_sculpt_paint.brush tool = brush.gpencil_sculpt_tool - if tool in {'SMOOTH', 'RANDOMIZE'}: - layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options") + if tool != 'CLONE': + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover") layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': if is_valid_context: @@ -151,6 +151,11 @@ class VIEW3D_HT_tool_header(Header): row.popover(panel="VIEW3D_PT_sculpt_symmetry_for_topbar", text="") elif mode_string == 'PAINT_VERTEX': row.popover(panel="VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar", text="") + elif mode_string == 'SCULPT_CURVES': + _row, sub = row_for_mirror() + sub.prop(context.object.data, "use_mirror_x", text="X", toggle=True) + sub.prop(context.object.data, "use_mirror_y", text="Y", toggle=True) + sub.prop(context.object.data, "use_mirror_z", text="Z", toggle=True) # Expand panels from the side-bar as popovers. popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"} @@ -511,9 +516,10 @@ 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") @@ -2037,7 +2043,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' @@ -2051,18 +2057,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): @@ -2217,8 +2219,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') @@ -3149,7 +3149,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") @@ -7656,7 +7658,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 570d7c12e30..698f0dadc33 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -3,7 +3,7 @@ # <pep8 compliant> from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( - GreasePencilSculptOptionsPanel, + GreasePencilSculptAdvancedPanel, GreasePencilDisplayPanel, GreasePencilBrushFalloff, ) @@ -1049,6 +1049,36 @@ 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" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'CURVES' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + curves = context.object.data + + row = layout.row(align=True, heading="Mirror") + row.prop(curves, "use_mirror_x", text="X", toggle=True) + 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' + bl_label = "Symmetry" + + draw = VIEW3D_PT_curves_sculpt_symmetry.draw + + # ********** default tools for weight-paint **************** @@ -1907,6 +1937,41 @@ class VIEW3D_PT_tools_grease_pencil_brush_sculpt_falloff(GreasePencilBrushFallof return (settings and settings.brush and settings.brush.curve) +class VIEW3D_PT_tools_grease_pencil_sculpt_brush_advanced(GreasePencilSculptAdvancedPanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_label = "Advanced" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' + bl_category = "Tool" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.tool_settings.gpencil_sculpt_paint.brush + if brush is None: + return False + + tool = brush.gpencil_sculpt_tool + return tool != 'CLONE' + + +class VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover(GreasePencilSculptAdvancedPanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_label = "Brush" + bl_category = "Tool" + + @classmethod + def poll(cls, context): + if context.region.type != 'TOOL_HEADER': + return False + + brush = context.tool_settings.gpencil_sculpt_paint.brush + if brush is None: + return False + + tool = brush.gpencil_sculpt_tool + return tool != 'CLONE' + + # Grease Pencil weight painting tools class GreasePencilWeightPanel: bl_context = ".greasepencil_weight" @@ -2240,13 +2305,6 @@ class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel): col.template_palette(settings, "palette", color=True) -class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, Panel, View3DPanel): - bl_context = ".greasepencil_sculpt" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' - bl_category = "Tool" - bl_label = "Sculpt Strokes" - - # Grease Pencil Brush Appearance (one for each mode) class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_paint" @@ -2323,6 +2381,9 @@ classes = ( VIEW3D_PT_sculpt_options, VIEW3D_PT_sculpt_options_gravity, + VIEW3D_PT_curves_sculpt_symmetry, + VIEW3D_PT_curves_sculpt_symmetry_for_topbar, + VIEW3D_PT_tools_weightpaint_symmetry, VIEW3D_PT_tools_weightpaint_symmetry_for_topbar, VIEW3D_PT_tools_weightpaint_options, @@ -2357,7 +2418,8 @@ classes = ( VIEW3D_PT_tools_grease_pencil_paint_appearance, VIEW3D_PT_tools_grease_pencil_sculpt_select, VIEW3D_PT_tools_grease_pencil_sculpt_settings, - VIEW3D_PT_tools_grease_pencil_sculpt_options, + VIEW3D_PT_tools_grease_pencil_sculpt_brush_advanced, + VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover, VIEW3D_PT_tools_grease_pencil_sculpt_appearance, VIEW3D_PT_tools_grease_pencil_weight_paint_select, VIEW3D_PT_tools_grease_pencil_weight_paint_settings, 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_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 15fdc73adeb..ab13a2e85d0 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -17,15 +17,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 302 +#define BLENDER_VERSION 303 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 14 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file 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 1e96c0e4c41..bf2d50f63be 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -48,6 +48,13 @@ struct BasisCache { * In other words, the index of the first control point that influences this evaluated point. */ Vector<int> start_indices; + + /** + * The result of #check_valid_size_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. + */ + bool invalid = false; }; } // namespace curves::nurbs @@ -174,11 +181,18 @@ class CurvesGeometry : public ::CurvesGeometry { /** Update the cached count of curves of each type, necessary after #curve_types_for_write. */ void update_curve_types(); - bool has_curve_with_type(const CurveType type) const; + bool has_curve_with_type(CurveType type) const; /** Return true if all of the curves have the provided type. */ bool is_single_type(CurveType type) const; /** Return the number of curves with each type. */ const std::array<int, CURVE_TYPES_NUM> &curve_type_counts() const; + /** + * All of the curve indices for curves with a specific type. + */ + IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; + IndexMask indices_for_curve_type(CurveType type, + IndexMask selection, + Vector<int64_t> &r_indices) const; Span<float3> positions() const; MutableSpan<float3> positions_for_write(); @@ -276,11 +290,6 @@ class CurvesGeometry : public ::CurvesGeometry { bool bounds_min_max(float3 &min, float3 &max) const; private: - /** - * All of the curve indices for curves with a specific type. - */ - IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; - /* -------------------------------------------------------------------- * Evaluation. */ @@ -760,7 +769,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, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)}; + return {start, curves::curve_segment_size(points.size(), cyclic)}; } inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, @@ -775,8 +784,7 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in const bool cyclic) const { const Span<float> lengths = this->evaluated_lengths_for_curve(curve_index, cyclic); - /* Check for curves that have no evaluated segments. */ - return lengths.is_empty() ? 0.0f : lengths.last(); + return lengths.last(); } /** \} */ 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_lib_principle_properties.h b/source/blender/blenkernel/BKE_lib_principle_properties.h index 10d0d33a0c4..42177204efb 100644 --- a/source/blender/blenkernel/BKE_lib_principle_properties.h +++ b/source/blender/blenkernel/BKE_lib_principle_properties.h @@ -40,7 +40,7 @@ struct ReportList; struct IDPrincipleProperties *BKE_lib_principleprop_init(struct ID *id); #if 0 /** - * Shallow or deep copy of a whole princple properties from \a src_id to \a dst_id. + * Shallow or deep copy of a whole principle properties from \a src_id to \a dst_id. */ void BKE_lib_principleprop_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy); #endif 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_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 881e86ccc54..a38bfb497a2 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -113,7 +113,7 @@ typedef enum ModifierApplyFlag { /** Render time. */ MOD_APPLY_RENDER = 1 << 0, /** Result of evaluation will be cached, so modifier might - * want to cache data for quick updates (used by subsurf) */ + * want to cache data for quick updates (used by subdivision-surface) */ MOD_APPLY_USECACHE = 1 << 1, /** Modifier evaluated for undeformed texture coordinates */ MOD_APPLY_ORCO = 1 << 2, 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_paint.h b/source/blender/blenkernel/BKE_paint.h index 0e976f04dd1..03cbcf575c3 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -733,7 +733,7 @@ enum { /* paint_vertex.cc */ /** - * Fills the object's active color atribute layer with the fill color. + * Fills the object's active color attribute layer with the fill color. * * \param[in] ob: The object. * \param[in] fill_color: The fill color. diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index a57d4d0a2bf..c9e88362b80 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -242,8 +242,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/pointcache.c diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index 555c4690308..e2f22ef00d3 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -1564,7 +1564,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, } if (GS(old_id->name) == ID_KE) { - /* Shape Keys are handled as part of their owning obdata (see below). This implies thar + /* Shape Keys are handled as part of their owning obdata (see below). This implies that * there is no way to know when the old pointer gets invalid, so just clear it immediately. */ item->userdata = NULL; 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_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 0114c0b45f4..45440358221 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -36,7 +36,7 @@ int calculate_evaluated_size(const int points_num, const KnotsMode knots_mode) { if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) { - return 0; + return points_num; } return resolution * curve_segment_size(points_num, cyclic); } @@ -232,8 +232,12 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, const GSpan src, GMutableSpan dst) { - BLI_assert(dst.size() == basis_cache.start_indices.size()); + if (basis_cache.invalid) { + dst.copy_from(src); + return; + } + BLI_assert(dst.size() == basis_cache.start_indices.size()); attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { 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 88e0bae0c13..ef921797698 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -668,12 +668,12 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, Vector<std::byte> eval_buffer; - Curves main_id = {nullptr}; + Curves main_id = {{nullptr}}; main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main); CurveComponent main_component; main_component.replace(&main_id, GeometryOwnershipType::Editable); - Curves profile_id = {nullptr}; + Curves profile_id = {{nullptr}}; profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile); CurveComponent profile_component; profile_component.replace(&profile_id, GeometryOwnershipType::Editable); diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 7a09b87490b..4dd0fad57d4 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -65,6 +65,8 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) 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_size; } /** @@ -250,6 +252,10 @@ void CurvesGeometry::fill_curve_types(const CurveType type) void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType type) { + if (selection.size() == this->curves_num()) { + this->fill_curve_types(type); + return; + } /* A potential performance optimization is only counting the changed indices. */ this->curve_types_for_write().fill_indices(selection, type); this->update_curve_types(); @@ -527,19 +533,23 @@ Span<int> CurvesGeometry::evaluated_offsets() const IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, Vector<int64_t> &r_indices) const { + return this->indices_for_curve_type(type, this->curves_range(), r_indices); +} - VArray<int8_t> types = this->curve_types(); +IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, + const IndexMask selection, + Vector<int64_t> &r_indices) const +{ + if (this->curve_type_counts()[type] == this->curves_num()) { + return selection; + } + const VArray<int8_t> types = this->curve_types(); if (types.is_single()) { - if (types.get_internal_single() == type) { - return IndexMask(types.size()); - } - return {}; + 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( - IndexMask(types.size()), 1024, r_indices, [&](const int index) { - return types_span[index] == type; - }); + selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; }); } void CurvesGeometry::ensure_nurbs_basis_cache() const @@ -577,6 +587,11 @@ 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)) { + 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); curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); @@ -696,9 +711,6 @@ Span<float3> CurvesGeometry::evaluated_tangents() const threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points), cyclic[curve_index], tangents.slice(evaluated_points)); @@ -773,9 +785,6 @@ Span<float3> CurvesGeometry::evaluated_normals() const for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } switch (normal_mode[curve_index]) { case NORMAL_MODE_Z_UP: curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points), @@ -916,9 +925,6 @@ void CurvesGeometry::ensure_evaluated_lengths() const for (const int curve_index : curves_range) { const bool cyclic = curves_cyclic[curve_index]; const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic); length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points), cyclic, @@ -1196,6 +1202,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } }); + new_curves.update_curve_types(); + return new_curves; } diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index bc9bba3ee2f..7c4a7db6462 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 /** \} */ 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/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/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_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/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/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/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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 27890a908ab..6ba97caa0ae 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -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; } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index e6a214452fe..7fd72fec3d0 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)) { @@ -2772,5 +2779,260 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* 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/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_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 50302eaa87a..161555208f3 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -68,7 +68,7 @@ BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer) } /* The number of render result passes are needed to store a single cryptomatte layer. Per - * renderpass 2 cryptomatte samples can be stored. */ + * render-pass 2 cryptomatte samples can be stored. */ BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer) { const int num_cryptomatte_levels = view_layer->cryptomatte_levels; diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 43d0b050cc8..a4bd789438d 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -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_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 6128556d364..6b9dc6fb017 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -301,7 +301,7 @@ typedef struct EEVEE_PassList { struct DRWPass *maxz_copydepth_ps; struct DRWPass *maxz_copydepth_layer_ps; - /* Renderpass Accumulation. */ + /* Render-pass Accumulation. */ struct DRWPass *material_accum_ps; struct DRWPass *background_accum_ps; struct DRWPass *cryptomatte_ps; @@ -1067,7 +1067,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; GPUTexture *renderpass_transmittance_input; - /* Renderpass ubo reference used by material pass. */ + /* Render-pass UBO reference used by material pass. */ struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ struct DRWView *cube_views[6]; diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 35eb33671db..7141928a20d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -11,9 +11,11 @@ #pragma once -/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */ -/* Current limiting factor is the sorting phase which is single pass and only sort within a - * threadgroup which maximum size is 1024. */ +/** + Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. + * Current limiting factor is the sorting phase which is single pass and only sort within a + * thread-group which maximum size is 1024. + */ #define CULLING_BATCH_SIZE 1024 /** diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 922f6c9e1ae..8fef1cf7fc5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -21,9 +21,9 @@ namespace blender::eevee { /* -------------------------------------------------------------------- */ -/** \name Init +/** \name Initialization * - * Init funcions need to be called once at the start of a frame. + * Initialization functions need to be called once at the start of a frame. * Active camera, render extent and enabled render passes are immutable until next init. * This takes care of resizing output buffers and view in case a parameter changed. * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources. diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 56f9b077f7a..af9ff6bf6ba 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -104,7 +104,7 @@ static inline eMaterialGeometry to_material_geometry(const Object *ob) } } -/** Unique key to identify each material in the hashmap. */ +/** Unique key to identify each material in the hash-map. */ struct MaterialKey { Material *mat; uint64_t options; @@ -169,7 +169,7 @@ struct ShaderKey { /** \} */ /* -------------------------------------------------------------------- */ -/** \name Default Material Nodetree +/** \name Default Material Node-Tree * * In order to support materials without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the Material even if @@ -254,4 +254,4 @@ class MaterialModule { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 086c5f9f358..39c5cf6a536 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -25,7 +25,7 @@ ShaderModule *ShaderModule::g_shader_module = nullptr; ShaderModule *ShaderModule::module_get() { if (g_shader_module == nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ g_shader_module = new ShaderModule(); } return g_shader_module; @@ -34,7 +34,7 @@ ShaderModule *ShaderModule::module_get() void ShaderModule::module_free() { if (g_shader_module != nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ delete g_shader_module; g_shader_module = nullptr; } @@ -148,7 +148,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu /** Noop. */ break; case MAT_GEOM_CURVES: - /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */ + /** Hair attributes come from sampler buffer. Transfer attributes to sampler. */ for (auto &input : info.vertex_inputs_) { if (input.name == "orco") { /** NOTE: Orco is generated from strand position for now. */ @@ -163,14 +163,14 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu case MAT_GEOM_WORLD: /** * Only orco layer is supported by world and it is procedurally generated. These are here to - * make the attribs_load function calls valids. + * make the attribs_load function calls valid. */ ATTR_FALLTHROUGH; case MAT_GEOM_GPENCIL: /** * Only one uv and one color attribute layer are supported by gpencil objects and they are * already declared in another createInfo. These are here to make the attribs_load - * function calls valids. + * function calls valid. */ for (auto &input : info.vertex_inputs_) { global_vars << input.type << " " << input.name << ";\n"; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index ba7c97b3b6a..29fcbafb167 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -36,7 +36,7 @@ class ShaderModule { private: std::array<GPUShader *, MAX_SHADER_TYPE> shaders_; - /** Shared shader module accross all engine instances. */ + /** Shared shader module across all engine instances. */ static ShaderModule *g_shader_module; public: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 2225ccac43a..45b067d203e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -27,7 +27,7 @@ using draw::TextureFromPool; #define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 /* -------------------------------------------------------------------- */ -/** \name Raytracing +/** \name Ray-Tracing * \{ */ enum eClosureBits : uint32_t { diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index 51e0f86fe5c..34357193d3e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -27,7 +27,7 @@ class Instance; /* -------------------------------------------------------------------- */ /** \name ObjectKey * - * Unique key to identify each object in the hashmap. + * Unique key to identify each object in the hash-map. * \{ */ struct ObjectKey { diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index df45200c712..de7341f814b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -9,7 +9,7 @@ * - The entire main view. * - A fragment of the main view (for panoramic projections). * - A shadow map view. - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -40,7 +40,7 @@ void ShadingView::sync(int2 render_extent_) int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y; /* Divide pixel count between the 6 views. Rendering to a square target. */ extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6))); - /* TODO(fclem) Clip unused views heres. */ + /* TODO(@fclem): Clip unused views here. */ is_enabled_ = true; } else { @@ -60,8 +60,8 @@ void ShadingView::sync(int2 render_extent_) const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr(); #if 0 if (false /* inst_.camera.is_panoramic() */) { - /* TODO(fclem) Overscans. */ - /* For now a mandatory 5% overscan for DoF. */ + /* TODO(@fclem) Over-scans. */ + /* For now a mandatory 5% over-scan for DoF. */ float side = data.clip_near * 1.05f; float near = data.clip_near; float far = data.clip_far; @@ -205,4 +205,4 @@ void ShadingView::update_view() /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index ab7b5722de1..e78a3222d8b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -8,7 +8,7 @@ * A view is either: * - The entire main view. * - A portion of the main view (for panoramic projections). - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -154,4 +154,4 @@ class MainView { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh index 56554051eea..05177928436 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.hh +++ b/source/blender/draw/engines/eevee_next/eevee_world.hh @@ -18,7 +18,7 @@ namespace blender::eevee { class Instance; /* -------------------------------------------------------------------- */ -/** \name Default World Nodetree +/** \name Default World Node-Tree * * In order to support worlds without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the World even if 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 e829a41d682..cd297de5719 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 @@ -94,7 +94,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_source("eevee_surf_forward_frag.glsl") // .additional_info("eevee_sampling_data", // "eevee_lightprobe_data", - /* Optionnally added depending on the material. */ + /* Optionally added depending on the material. */ // "eevee_raytrace_data", // "eevee_transmittance_data", // "eevee_utility_texture", diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 5c5226bfe65..072d32c41cd 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); diff --git a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh index a765d881682..b9b1b73dbd4 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh @@ -284,7 +284,7 @@ GPU_SHADER_INTERFACE_INFO(overlay_particle_iface, "").flat(Type::VEC4, "finalCol GPU_SHADER_CREATE_INFO(overlay_particle) .sampler(0, ImageType::FLOAT_1D, "weightTex") - .push_constant(Type::VEC4, "color") /* Drawsize packed in alpha */ + .push_constant(Type::VEC4, "color") /* Draw-size packed in alpha. */ .vertex_in(0, Type::VEC3, "part_pos") .vertex_in(1, Type::VEC4, "part_rot") .vertex_in(2, Type::FLOAT, "part_val") diff --git a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh index 713c8c2dc4b..5853e974eeb 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh @@ -48,7 +48,7 @@ GPU_SHADER_CREATE_INFO(overlay_volume_velocity_needle) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Volume Gridlines +/** \name Volume Grid-Lines * \{ */ GPU_SHADER_INTERFACE_INFO(overlay_volume_gridlines_iface, "").flat(Type::VEC4, "finalColor"); diff --git a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh index 43367121d6a..16b59f6bb7d 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh @@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(overlay_wireframe) .sampler(0, ImageType::DEPTH_2D, "depthTex") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") - .vertex_in(2, Type::FLOAT, "wd") /* wiredata */ + .vertex_in(2, Type::FLOAT, "wd") /* wire-data. */ .vertex_out(overlay_wireframe_iface) .vertex_source("wireframe_vert.glsl") .fragment_source("wireframe_frag.glsl") diff --git a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh index ad0de61ffc3..e3166582197 100644 --- a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh +++ b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh @@ -3,7 +3,7 @@ #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ -/** \name Select ID fo Edit Mesh selection +/** \name Select ID for Edit Mesh Selection * \{ */ GPU_SHADER_INTERFACE_INFO(select_id_iface, "").flat(Type::INT, "id"); diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index d803fad5052..6f571084620 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. */ 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/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 7bb1d652bf1..c2d517588b2 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4020,6 +4020,8 @@ static bool acf_nlaaction_setting_valid(bAnimContext *UNUSED(ac), else { return false; } + case ACHANNEL_SETTING_SELECT: /* selected */ + return true; /* unsupported */ default: @@ -4040,6 +4042,9 @@ static int acf_nlaaction_setting_flag(bAnimContext *UNUSED(ac), *neg = true; /* XXX */ return ADT_NLA_EDIT_NOMAP; + case ACHANNEL_SETTING_SELECT: /* selected */ + return ADT_UI_SELECTED; + default: /* unsupported */ return 0; } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 31d90c8bfec..f148bb5b77d 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -125,6 +125,7 @@ void ANIM_set_active_channel(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale->adt) { @@ -182,6 +183,7 @@ void ANIM_set_active_channel(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale && ale->adt) { @@ -199,7 +201,6 @@ void ANIM_set_active_channel(bAnimContext *ac, /* unhandled currently, but may be interesting */ case ANIMTYPE_MASKLAYER: case ANIMTYPE_SHAPEKEY: - case ANIMTYPE_NLAACTION: break; /* other types */ @@ -312,6 +313,7 @@ static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListB case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) { return ACHANNEL_SETFLAG_CLEAR; @@ -420,6 +422,7 @@ static void anim_channels_select_set(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale->adt) { 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/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 9e7e00d5656..d8ecb2c5ae0 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -777,6 +777,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_cut ops.curves.sculpt_delete ops.curves.sculpt_grow_shrink + ops.curves.sculpt_puff ops.curves.sculpt_snake_hook ops.generic.cursor ops.generic.select diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 05f9e19da71..cfc158b117f 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -385,24 +385,16 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) ot->srna, "name", "Color", MAX_NAME, "Name", "Name of new color attribute"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, - {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, - {0, nullptr, 0, nullptr, nullptr}}; - - static EnumPropertyItem types[3] = {{CD_PROP_COLOR, "COLOR", 0, "Color", ""}, - {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, - {0, nullptr, 0, nullptr, nullptr}}; - prop = RNA_def_enum(ot->srna, "domain", - domains, + rna_enum_color_attribute_domain_items, ATTR_DOMAIN_POINT, "Domain", "Type of element that attribute is stored on"); prop = RNA_def_enum(ot->srna, "data_type", - types, + rna_enum_color_attribute_type_items, CD_PROP_COLOR, "Data Type", "Type of data stored in attribute"); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 3cdf364e4b2..01ac02a9a1d 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -146,6 +146,10 @@ typedef struct tGP_BrushEditData { float inv_mat[4][4]; RNG *rng; + /* Auto-masking strokes. */ + struct GHash *automasking_strokes; + bool automasking_ready; + } tGP_BrushEditData; /* Callback for performing some brush operation on a single point */ @@ -1182,9 +1186,18 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->region = CTX_wm_region(C); Paint *paint = &ts->gp_sculptpaint->paint; - gso->brush = paint->brush; + Brush *brush = paint->brush; + gso->brush = brush; BKE_curvemapping_init(gso->brush->curve); + if (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) { + gso->automasking_strokes = BLI_ghash_ptr_new(__func__); + } + else { + gso->automasking_strokes = NULL; + } /* save mask */ gso->mask = ts->gpencil_selectmode_sculpt; @@ -1285,6 +1298,10 @@ static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op) BLI_rng_free(gso->rng); } + if (gso->automasking_strokes != NULL) { + BLI_ghash_free(gso->automasking_strokes, NULL, NULL); + } + /* Disable headerprints. */ ED_workspace_status_text(C, NULL); @@ -1570,11 +1587,15 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, bool redo_geom = false; Object *ob = gso->object; bGPdata *gpd = ob->data; - char tool = gso->brush->gpencil_sculpt_tool; + const char tool = gso->brush->gpencil_sculpt_tool; GP_SpaceConversion *gsc = &gso->gsc; Brush *brush = gso->brush; const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : gso->brush->size; + const bool is_automasking = (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) != 0; /* Calc bound box matrix. */ float bound_mat[4][4]; BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); @@ -1589,6 +1610,13 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, continue; } + { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + if ((is_automasking) && (!BLI_ghash_haskey(gso->automasking_strokes, gps_active))) { + continue; + } + } + /* Check if the stroke collide with brush. */ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { continue; @@ -1699,6 +1727,132 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, return changed; } +/* Get list of Auto-Masking strokes. */ +static bool get_automasking_strokes_list(tGP_BrushEditData *gso) +{ + bGPdata *gpd = gso->gpd; + GP_SpaceConversion *gsc = &gso->gsc; + Brush *brush = gso->brush; + Object *ob = gso->object; + Material *mat_active = BKE_gpencil_material(ob, ob->actcol); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_masking_stroke = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_STROKE) != 0; + const bool is_masking_layer = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER) != 0; + const bool is_masking_material = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL) != 0; + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + + /* Define a fix number of pixel as cursor radius. */ + const int radius = 10; + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* Calculate bound box matrix. */ + float bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); + + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->totpoints == 0) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(gso->object, gpl, gps) == false) { + continue; + } + + /* Layer Auto-Masking. */ + if ((is_masking_layer) && (gpl == gpl_active)) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + continue; + } + /* Material Auto-Masking. */ + if (is_masking_material) { + Material *mat = BKE_object_material_get(ob, gps->mat_nr + 1); + if (mat == mat_active) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + continue; + } + } + + /* If Stroke Auto-Masking is not enabled, nothing else to do. */ + if (!is_masking_stroke) { + continue; + } + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { + continue; + } + + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + bGPDspoint npt; + + if (gps->totpoints == 1) { + gpencil_point_to_parent_space(gps->points, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + /* Only check if point is inside. */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke. + */ + for (int i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with. */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Check first point. */ + gpencil_point_to_parent_space(pt1, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + if (len_v2v2_int(mval_i, pc1) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + + /* Check second point. */ + gpencil_point_to_parent_space(pt2, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + if (len_v2v2_int(mval_i, pc2) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + + /* Check segment. */ + if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + + return true; +} + /* Perform two-pass brushes which modify the existing strokes */ static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) { @@ -1840,6 +1994,14 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA * gso->brush_rect.xmax = mouse[0] + radius; gso->brush_rect.ymax = mouse[1] + radius; + /* Get list of Auto-Masking strokes. */ + if ((!gso->automasking_ready) && + (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL))) { + gso->automasking_ready = get_automasking_strokes_list(gso); + } + /* Apply brush */ char tool = gso->brush->gpencil_sculpt_tool; if (tool == GPSCULPT_TOOL_CLONE) { diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4c01c75e06c..54d36f44f82 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]); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 1e9b68c0920..414643dd0d6 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -952,6 +952,22 @@ float ED_view3d_select_dist_px(void); void ED_view3d_viewcontext_init(struct bContext *C, struct ViewContext *vc, struct Depsgraph *depsgraph); + +/** + * Re-initialize `vc` with `obact` as if it's active object (with some differences). + * + * This is often used when operating on multiple objects in modes (edit, pose mode etc) + * where the `vc` is passed in as an argument which then references it's object data. + * + * \note members #ViewContext.obedit & #ViewContext.em are only initialized if they're already set, + * by #ED_view3d_viewcontext_init in most cases. + * This is necessary because the active object defines the current object-mode. + * When iterating over objects in object-mode it doesn't make sense to perform + * an edit-mode action on an object that happens to contain edit-mode data. + * In some cases these values are cleared allowing the owner of `vc` to explicitly + * disable edit-mode operation (to force object selection in edit-mode for e.g.). + * So object-mode specific values should remain cleared when initialized with another object. + */ void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact); /** * Use this call when executing an operator, 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/io_alembic.c b/source/blender/editors/io/io_alembic.c index fd454083653..87923d9fdf8 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -282,6 +282,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->ui = wm_alembic_export_draw; ot->check = wm_alembic_export_check; + ot->flag |= OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, @@ -475,7 +476,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) /* This dummy prop is used to check whether we need to init the start and * end frame values to that of the scene's, otherwise they are reset at * every change, draw update. */ - RNA_def_boolean(ot->srna, "init_scene_frame_range", false, "", ""); + RNA_def_boolean(ot->srna, "init_scene_frame_range", true, "", ""); } /* ************************************************************************** */ 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/object/object_add.cc b/source/blender/editors/object/object_add.cc index db8860efdd8..820f500d172 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -2116,6 +2116,21 @@ 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) { + return false; + } + return true; +} + void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) { ot->name = "Add Empty Curves"; @@ -2123,7 +2138,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 +2777,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) { @@ -3065,6 +3103,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; @@ -3480,6 +3562,7 @@ 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, 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_modes.c b/source/blender/editors/object/object_modes.c index cc6aa34d39d..fdf2cae026d 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -143,7 +143,12 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } break; case OB_CURVES: - if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) { + if (U.experimental.use_new_curves_tools) { + if (mode & OB_MODE_EDIT) { + return true; + } + } + if (mode & OB_MODE_SCULPT_CURVES) { return true; } break; diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 08eed52f440..d3bf28798c4 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -27,8 +27,8 @@ set(INC ) set(SRC - curves_sculpt_3d_brush.cc curves_sculpt_add.cc + curves_sculpt_brush.cc curves_sculpt_comb.cc curves_sculpt_delete.cc curves_sculpt_grow_shrink.cc diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 0d399419ad8..1fdecf47bbd 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -21,6 +21,7 @@ #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" @@ -107,7 +108,7 @@ struct AddOperationExecutor { bool use_interpolation_; float new_curve_length_; int add_amount_; - int points_per_curve_ = 8; + int points_per_curve_; /** Various matrices to convert between coordinate spaces. */ float4x4 curves_to_world_mat_; @@ -171,6 +172,7 @@ struct AddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); + 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_; @@ -183,7 +185,9 @@ struct AddOperationExecutor { return; } - RandomNumberGenerator rng{(uint32_t)(PIL_check_seconds_timer() * 1000000.0f)}; + const double time = PIL_check_seconds_timer() * 1000000.0; + /* Use a pointer cast to avoid overflow warnings. */ + RandomNumberGenerator rng{*(uint32_t *)(&time)}; BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); @@ -194,13 +198,13 @@ struct AddOperationExecutor { /* Sample points on the surface using one of multiple strategies. */ AddedPoints added_points; if (add_amount_ == 1) { - this->sample_in_center(added_points); + this->sample_in_center_with_symmetry(added_points); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected(rng, added_points); + this->sample_projected_with_symmetry(rng, added_points); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical(rng, added_points); + this->sample_spherical_with_symmetry(rng, added_points); } else { BLI_assert_unreachable(); @@ -241,13 +245,27 @@ struct AddOperationExecutor { /** * Sample a single point exactly at the mouse position. */ - void sample_in_center(AddedPoints &r_added_points) + void sample_in_center_with_symmetry(AddedPoints &r_added_points) { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true); const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_in_center( + r_added_points, brush_transform * ray_start_su, brush_transform * ray_end_su); + } + } + + void sample_in_center(AddedPoints &r_added_points, + const float3 &ray_start_su, + const float3 &ray_end_su) + { const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; @@ -280,11 +298,23 @@ struct AddOperationExecutor { /** * Sample points by shooting rays within the brush radius in the 3D view. */ - void sample_projected(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_projected(rng, r_added_points, brush_transform); + } + } + + void sample_projected(RandomNumberGenerator &rng, + AddedPoints &r_added_points, + const float4x4 &brush_transform) { + const int old_amount = r_added_points.bary_coords.size(); const int max_iterations = std::max(100'000, add_amount_ * 10); int current_iteration = 0; - while (r_added_points.bary_coords.size() < add_amount_) { + while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } @@ -296,8 +326,8 @@ struct AddOperationExecutor { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true); - const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; - const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + const float3 ray_start_su = brush_transform * (world_to_surface_mat_ * ray_start_wo); + const float3 ray_end_su = brush_transform * (world_to_surface_mat_ * ray_end_wo); const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; @@ -339,7 +369,7 @@ struct AddOperationExecutor { /** * Sample points in a 3D sphere around the surface position that the mouse hovers over. */ - void sample_spherical(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) { /* Find ray that starts in the center of the brush. */ float3 brush_ray_start_wo, brush_ray_end_wo; @@ -347,7 +377,6 @@ struct AddOperationExecutor { depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true); const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo; const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo; - const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius * in 3D. */ @@ -362,6 +391,27 @@ struct AddOperationExecutor { const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo; const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo; + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_spherical(rng, + r_added_points, + brush_transform * brush_ray_start_su, + brush_transform * brush_ray_end_su, + brush_transform * brush_radius_ray_start_su, + brush_transform * brush_radius_ray_end_su); + } + } + + void sample_spherical(RandomNumberGenerator &rng, + AddedPoints &r_added_points, + const float3 &brush_ray_start_su, + const float3 &brush_ray_end_su, + const float3 &brush_radius_ray_start_su, + const float3 &brush_radius_ray_end_su) + { + const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); + BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; @@ -426,7 +476,8 @@ struct AddOperationExecutor { const int max_iterations = 5; int current_iteration = 0; - while (r_added_points.bary_coords.size() < add_amount_) { + const int old_amount = r_added_points.bary_coords.size(); + while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } @@ -506,8 +557,8 @@ struct AddOperationExecutor { } /* Remove samples when there are too many. */ - while (r_added_points.bary_coords.size() > add_amount_) { - const int index_to_remove = rng.get_int32(r_added_points.bary_coords.size()); + while (r_added_points.bary_coords.size() > old_amount + add_amount_) { + const int index_to_remove = rng.get_int32(add_amount_) + old_amount; r_added_points.bary_coords.remove_and_reorder(index_to_remove); r_added_points.looptri_indices.remove_and_reorder(index_to_remove); r_added_points.positions_cu.remove_and_reorder(index_to_remove); @@ -786,8 +837,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_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 945bb09c0c6..89470772e1c 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -116,8 +116,11 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re); + float3 brush_position_cu; + closest_to_line_segment_v3(brush_position_cu, closest_cu, ray_start_cu, ray_end_cu); + BrushPositionCandidate candidate; - candidate.position_cu = closest_cu; + candidate.position_cu = brush_position_cu; candidate.depth_sq_cu = depth_sq_cu; candidate.distance_sq_re = distance_sq_re; @@ -229,4 +232,32 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, return brush_3d; } +Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry) +{ + Vector<float4x4> matrices; + + auto symmetry_to_factors = [&](const eCurvesSymmetryType type) -> Span<float> { + if (symmetry & type) { + static std::array<float, 2> values = {1.0f, -1.0f}; + return values; + } + static std::array<float, 1> values = {1.0f}; + return values; + }; + + for (const float x : symmetry_to_factors(CURVES_SYMMETRY_X)) { + for (const float y : symmetry_to_factors(CURVES_SYMMETRY_Y)) { + for (const float z : symmetry_to_factors(CURVES_SYMMETRY_Z)) { + float4x4 matrix = float4x4::identity(); + matrix.values[0][0] = x; + matrix.values[1][1] = y; + matrix.values[2][2] = z; + matrices.append(matrix); + } + } + } + + return matrices; +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 232d632aa3f..28258a26f74 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -173,10 +173,10 @@ struct CombOperationExecutor { EnumerableThreadSpecific<Vector<int>> changed_curves; if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { - this->comb_projected(changed_curves); + this->comb_projected_with_symmetry(changed_curves); } else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - this->comb_spherical(changed_curves); + this->comb_spherical_with_symmetry(changed_curves); } else { BLI_assert_unreachable(); @@ -192,8 +192,20 @@ struct CombOperationExecutor { /** * Do combing in screen space. */ - void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->comb_projected(r_changed_curves, brush_transform); + } + } + + void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, + const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; @@ -207,7 +219,7 @@ struct CombOperationExecutor { bool curve_changed = false; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = positions_cu[point_i]; + const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; /* Find the position of the point in screen space. */ float2 old_pos_re; @@ -232,7 +244,8 @@ struct CombOperationExecutor { float3 new_position_wo; ED_view3d_win_to_3d( v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + const float3 new_position_cu = brush_transform * + (world_to_curves_mat_ * new_position_wo); positions_cu[point_i] = new_position_cu; curve_changed = true; @@ -247,10 +260,8 @@ struct CombOperationExecutor { /** * Do combing in 3D space. */ - void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -268,10 +279,26 @@ struct CombOperationExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float3 brush_diff_cu = brush_end_cu - brush_start_cu; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->comb_spherical(r_changed_curves, + brush_transform * brush_start_cu, + brush_transform * brush_end_cu, + brush_radius_cu); + } + } + + void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, + const float3 &brush_start_cu, + const float3 &brush_end_cu, + const float brush_radius_cu) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Vector<int> &local_changed_curves = r_changed_curves.local(); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 2841a19d677..3f3c48ecbb6 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -117,56 +117,70 @@ struct DeleteOperationExecutor { } } + Array<bool> curves_to_delete(curves_->curves_num(), false); if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->delete_projected(); + this->delete_projected_with_symmetry(curves_to_delete); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->delete_spherical(); + this->delete_spherical_with_symmetry(curves_to_delete); } else { BLI_assert_unreachable(); } + Vector<int64_t> indices; + const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + curves_->curves_range(), 4096, indices, [&](const int curve_i) { + return curves_to_delete[curve_i]; + }); + + curves_->remove_curves(mask); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); ED_region_tag_redraw(region_); } - void delete_projected() + void delete_projected_with_symmetry(MutableSpan<bool> curves_to_delete) { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->delete_projected(curves_to_delete, brush_transform); + } + } + + void delete_projected(MutableSpan<bool> curves_to_delete, const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); Span<float3> positions_cu = curves_->positions(); - /* Find indices of curves that have to be removed. */ - Vector<int64_t> indices; - const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( - curves_->curves_range(), 512, indices, [&](const int curve_i) { - const IndexRange point_range = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(point_range.size() - 1)) { - const float3 pos1_cu = positions_cu[point_range[segment_i]]; - const float3 pos2_cu = positions_cu[point_range[segment_i + 1]]; - - float2 pos1_re, pos2_re; - ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); - - const float dist = dist_seg_seg_v2( - pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); - if (dist <= brush_radius_re_) { - return true; - } + threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { + for (const int curve_i : curve_range) { + const IndexRange point_range = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(point_range.size() - 1)) { + const float3 pos1_cu = brush_transform_inv * positions_cu[point_range[segment_i]]; + const float3 pos2_cu = brush_transform_inv * positions_cu[point_range[segment_i + 1]]; + + float2 pos1_re, pos2_re; + ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); + + const float dist = dist_seg_seg_v2(pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); + if (dist <= brush_radius_re_) { + curves_to_delete[curve_i] = true; + break; } - return false; - }); - - curves_->remove_curves(curves_to_remove); + } + } + }); } - void delete_spherical() + void delete_spherical_with_symmetry(MutableSpan<bool> curves_to_delete) { - Span<float3> positions_cu = curves_->positions(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -184,35 +198,48 @@ struct DeleteOperationExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->delete_spherical( + curves_to_delete, brush_transform * brush_start_cu, brush_transform * brush_end_cu); + } + } + + void delete_spherical(MutableSpan<bool> curves_to_delete, + const float3 &brush_start_cu, + const float3 &brush_end_cu) + { + Span<float3> positions_cu = curves_->positions(); + const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); - Vector<int64_t> indices; - const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( - curves_->curves_range(), 512, indices, [&](const int curve_i) { - const IndexRange points = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(points.size() - 1)) { - const float3 pos1_cu = positions_cu[points[segment_i]]; - const float3 pos2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_segment_cu, closest_brush_cu; - isect_seg_seg_v3(pos1_cu, - pos2_cu, - brush_start_cu, - brush_end_cu, - closest_segment_cu, - closest_brush_cu); - const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, - closest_brush_cu); - if (distance_to_brush_sq_cu > brush_radius_sq_cu) { - continue; - } - return true; + threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { + for (const int curve_i : curve_range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 pos1_cu = positions_cu[points[segment_i]]; + const float3 pos2_cu = positions_cu[points[segment_i] + 1]; + + float3 closest_segment_cu, closest_brush_cu; + isect_seg_seg_v3(pos1_cu, + pos2_cu, + brush_start_cu, + brush_end_cu, + closest_segment_cu, + closest_brush_cu); + const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, + closest_brush_cu); + if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + continue; } - return false; - }); - - curves_->remove_curves(curves_to_remove); + curves_to_delete[curve_i] = true; + break; + } + } + }); } void initialize_spherical_brush_reference_point() diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 6228a643a76..4ac4559cbbc 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -367,6 +367,13 @@ struct CurvesEffectOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + Vector<float4x4> symmetry_brush_transforms_inv; + for (const float4x4 brush_transform : symmetry_brush_transforms) { + symmetry_brush_transforms_inv.append(brush_transform.inverted()); + } + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); @@ -374,55 +381,59 @@ struct CurvesEffectOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); const int tot_segments = points.size() - 1; float max_move_distance_cu = 0.0f; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; - - float2 p1_re, p2_re; - ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); - - float2 closest_on_brush_re; - float2 closest_on_segment_re; - float lambda_on_brush; - float lambda_on_segment; - const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, - closest_on_segment_re, - &lambda_on_brush, - &lambda_on_segment, - brush_pos_start_re_, - brush_pos_end_re_, - p1_re, - p2_re); - - if (dist_to_brush_sq_re > brush_radius_sq_re_) { - continue; - } - const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); - const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_re, brush_radius_re_); - const float weight = brush_strength_ * radius_falloff; - - const float3 closest_on_segment_cu = math::interpolate(p1_cu, p2_cu, lambda_on_segment); - - float3 brush_start_pos_wo, brush_end_pos_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, - brush_pos_start_re_, - brush_start_pos_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, - brush_pos_end_re_, - brush_end_pos_wo); - const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; - const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; - - const float move_distance_cu = weight * - math::distance(brush_start_pos_cu, brush_end_pos_cu); - max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = brush_transform_inv * positions_cu[points[segment_i]]; + const float3 &p2_cu = brush_transform_inv * positions_cu[points[segment_i] + 1]; + + float2 p1_re, p2_re; + ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); + + float2 closest_on_brush_re; + float2 closest_on_segment_re; + float lambda_on_brush; + float lambda_on_segment; + const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, + closest_on_segment_re, + &lambda_on_brush, + &lambda_on_segment, + brush_pos_start_re_, + brush_pos_end_re_, + p1_re, + p2_re); + + if (dist_to_brush_sq_re > brush_radius_sq_re_) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re_); + const float weight = brush_strength_ * radius_falloff; + + const float3 closest_on_segment_cu = math::interpolate( + p1_cu, p2_cu, lambda_on_segment); + + float3 brush_start_pos_wo, brush_end_pos_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_start_re_, + brush_start_pos_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_end_re_, + brush_end_pos_wo); + const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; + const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; + + const float move_distance_cu = weight * + math::distance(brush_start_pos_cu, brush_end_pos_cu); + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + } } if (max_move_distance_cu > 0.0f) { local_influences.curve_indices.append(curve_i); @@ -454,6 +465,10 @@ struct CurvesEffectOperationExecutor { const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); @@ -461,32 +476,37 @@ struct CurvesEffectOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); const int tot_segments = points.size() - 1; float max_move_distance_cu = 0.0f; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_on_segment_cu; - float3 closest_on_brush_cu; - isect_seg_seg_v3(p1_cu, - p2_cu, - brush_pos_start_cu, - brush_pos_end_cu, - closest_on_segment_cu, - closest_on_brush_cu); - - const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, - closest_on_brush_cu); - if (dist_to_brush_sq_cu > brush_radius_sq_cu) { - continue; + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu; + const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; + + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + const float3 &p2_cu = positions_cu[points[segment_i] + 1]; + + float3 closest_on_segment_cu; + float3 closest_on_brush_cu; + isect_seg_seg_v3(p1_cu, + p2_cu, + brush_pos_start_transformed_cu, + brush_pos_end_transformed_cu, + closest_on_segment_cu, + closest_on_brush_cu); + + const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, + closest_on_brush_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + + const float move_distance_cu = weight * brush_pos_diff_length_cu; + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); } - - const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); - const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_cu, brush_radius_cu); - const float weight = brush_strength_ * radius_falloff; - - const float move_distance_cu = weight * brush_pos_diff_length_cu; - max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); } if (max_move_distance_cu > 0.0f) { local_influences.curve_indices.append(curve_i); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 03413221907..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(); @@ -53,4 +53,6 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, const float2 &brush_pos_re, float brush_radius_re); +Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry); + } // namespace blender::ed::sculpt_paint 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/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index 6d930d35f04..d2814c17682 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -136,10 +136,10 @@ struct SnakeHookOperatorExecutor { } if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - this->spherical_snake_hook(); + this->spherical_snake_hook_with_symmetry(); } else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { - this->projected_snake_hook(); + this->projected_snake_hook_with_symmetry(); } else { BLI_assert_unreachable(); @@ -150,8 +150,19 @@ struct SnakeHookOperatorExecutor { ED_region_tag_redraw(region_); } - void projected_snake_hook() + void projected_snake_hook_with_symmetry() { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->projected_snake_hook(brush_transform); + } + } + + void projected_snake_hook(const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; @@ -161,7 +172,7 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = positions_cu[last_point_i]; + const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; float2 old_pos_re; ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); @@ -179,17 +190,15 @@ struct SnakeHookOperatorExecutor { float3 new_position_wo; ED_view3d_win_to_3d( v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + const float3 new_position_cu = brush_transform * (world_to_curves_mat_ * new_position_wo); this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu); } }); } - void spherical_snake_hook() + void spherical_snake_hook_with_symmetry() { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -206,9 +215,23 @@ struct SnakeHookOperatorExecutor { brush_end_wo); const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_cu = self_->brush_3d_.radius_cu; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->spherical_snake_hook( + brush_transform * brush_start_cu, brush_transform * brush_end_cu, brush_radius_cu); + } + } + + void spherical_snake_hook(const float3 &brush_start_cu, + const float3 &brush_end_cu, + const float brush_radius_cu) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 1303da71435..872dfe68793 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,33 @@ 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; + } + } + 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 +6674,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 +6746,48 @@ 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; + } + + 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 +6795,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/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 747295f3de0..16b22775b9e 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -4145,13 +4145,7 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_ } /** - * Fills the object's active color atribute layer with the fill color. - * - * \param[in] ob: The object. - * \param[in] fill_color: The fill color. - * \param[in] only_selected: Limit the fill to selected faces or vertices. - * - * \return #true if successful. + * See doc-string for #BKE_object_attributes_active_color_fill. */ static bool paint_object_attributes_active_color_fill_ex(Object *ob, ColorPaint4f fill_color, diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 5e4389279eb..f89bfd2a36a 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -107,6 +107,7 @@ bool nla_panel_context(const bContext *C, found = 1; break; } + case ANIMTYPE_NLAACTION: case ANIMTYPE_SCENE: /* Top-Level Widgets doubling up as datablocks */ case ANIMTYPE_OBJECT: case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ 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_intern.hh b/source/blender/editors/space_node/node_intern.hh index 4157176cd68..f1653e3dfd6 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -74,13 +74,17 @@ struct SpaceNode_Runtime { /** Mouse position for drawing socket-less links and adding nodes. */ float2 cursor; - /* Indicates that the compositing tree in the space needs to be re-evaluated using the + /** + * Indicates that the compositing tree in the space needs to be re-evaluated using the * auto-compositing pipeline. - * Takes priority over the regular compsiting. */ + * 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. */ + /** + * 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. */ @@ -100,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/space_node.cc b/source/blender/editors/space_node/space_node.cc index 296cd1ff133..348fb926d17 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -327,8 +327,9 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) /** * Tag the space to recalculate the compositing tree using auto-compositing pipeline. * - * Will check the space to be using a compsiting tree, and check whether auto-compositing - * is enabled. If the checks do not pass then the function has no affect. */ + * 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)) { @@ -347,7 +348,8 @@ static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *are * 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. */ + * 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)) { 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_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/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index ffd33006cc2..7a022441876 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -126,6 +126,7 @@ void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgra void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) { vc->obact = obact; + /* See public doc-string for rationale on checking the existing values first. */ if (vc->obedit) { BLI_assert(BKE_object_is_in_editmode(obact)); vc->obedit = obact; 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..e119b264ae7 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, + true, + &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/freestyle/intern/geometry/SweepLine.h b/source/blender/freestyle/intern/geometry/SweepLine.h index 1165e1bf064..442b7e97ed8 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> &UNUSED(brother)) { } virtual ~binary_rule() diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 6108629183c..f87c85f808f 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -65,6 +65,7 @@ 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 @@ -74,6 +75,22 @@ set(SRC ) +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/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index fcf1e28c6da..259e62a249c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -122,6 +122,8 @@ static void deformStroke(GpencilModifierData *md, const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); const bool invert_group = (mmd->flag & GP_NOISE_INVERT_VGROUP) != 0; const bool use_curve = (mmd->flag & GP_NOISE_CUSTOM_CURVE) != 0 && mmd->curve_intensity; + const int cfra = (int)DEG_get_ctime(depsgraph); + const bool is_keyframe = (mmd->noise_mode == GP_NOISE_RANDOM_KEYFRAME); if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -148,7 +150,13 @@ static void deformStroke(GpencilModifierData *md, seed += BLI_hash_string(md->name); if (mmd->flag & GP_NOISE_USE_RANDOM) { - seed += ((int)DEG_get_ctime(depsgraph)) / mmd->step; + if (!is_keyframe) { + seed += cfra / mmd->step; + } + else { + /* If change every keyframe, use the last keyframe. */ + seed += gpf->framenum; + } } /* Sanitize as it can create out of bound reads. */ @@ -302,7 +310,12 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_random")); - uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "random_mode", 0, NULL, ICON_NONE); + + const int mode = RNA_enum_get(ptr, "random_mode"); + if (mode != GP_NOISE_RANDOM_KEYFRAME) { + uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); + } } static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) 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..dbe2ae0b890 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) { - BMLoop *ll, *lr = NULL; + EdgeFeatReduceData *feat_chunk_join = (EdgeFeatReduceData *)chunk_join; + EdgeFeatReduceData *feat_chunk = (EdgeFeatReduceData *)chunk; + feat_chunk_join->feat_edges += feat_chunk->feat_edges; +} - ll = e->l; - if (ll) { - lr = e->l->radial_next; - } +static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict tls) +{ + 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; - if (!ll && !lr) { - return LRT_EDGE_FLAG_LOOSE; + uint16_t edge_flag_result = 0; + + /* 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; + } + } - /* 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; + int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr; + int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr; + + 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; } } - if (rb->use_crease) { - if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { + 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 && 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; + } + 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); } - return edge_flag_result; + 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; - if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm); - BMOperator findop, weldop; + BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings); - /* 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); + lineart_sort_adjacent_items(adj_e, total_edges); - BMO_op_exec(bm, &findop); + 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; + } + } - /* 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); + MEM_freeN(adj_e); + + return edge_nabr; +} + +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); + + int usage = ob_info->usage; - eln->element_count = bm->totface; - eln->object_ref = orig_ob; - eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); + 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; @@ -2228,8 +2493,8 @@ static void lineart_main_load_geometries( } 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; } @@ -2259,9 +2524,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 +3271,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 +3455,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_shader.h b/source/blender/gpu/GPU_shader.h index b620fe9cc9d..72a36ebfac4 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 pyapi 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..474205ce8c3 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 uniform 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/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..eb2d40f5da9 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -549,7 +549,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/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index e013bb5321a..47f03a5777b 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -242,7 +242,7 @@ class MTLTexture : public Texture { id<MTLBuffer> vert_buffer_mtl_; int vert_buffer_offset_; - /* Core parameters and subresources. */ + /* Core parameters and sub-resources. */ eGPUTextureUsage gpu_image_usage_flags_; /* Whether the texture's properties or state has changed (e.g. mipmap range), and re-baking of diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index df3efdd12e7..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 " @@ -1769,8 +1769,8 @@ void gpu::MTLTexture::ensure_baked() /* CUBE TEXTURES */ case GPU_TEXTURE_CUBE: case GPU_TEXTURE_CUBE_ARRAY: { - /* Note: For a cubemap 'Texture::d_' refers to total number of faces, not just array slices - */ + /* NOTE: For a cube-map 'Texture::d_' refers to total number of faces, + * not just array slices. */ BLI_assert(this->w_ > 0 && this->h_ > 0); this->texture_descriptor_ = [[MTLTextureDescriptor alloc] init]; this->texture_descriptor_.pixelFormat = mtl_format; 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/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_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh index 369fe3ac293..ae7edeb16e2 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh @@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_clipped_uniform_color) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .push_constant(Type::VEC4, "color") - /* TODO(fclem): Put thoses two to one UBO. */ + /* TODO(@fclem): Put those two to one UBO. */ .push_constant(Type::MAT4, "ModelMatrix") .push_constant(Type::VEC4, "ClipPlane") .vertex_source("gpu_shader_3D_clipped_uniform_color_vert.glsl") 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_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_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index a8b4b039370..8e878b6e14b 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,4 +1,4 @@ -#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, 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_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_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/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..80fcceb0aa7 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/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/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..02bd5b2b81f 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -7,8 +7,6 @@ set(INC ../../blenlib ../../depsgraph ../../makesdna - ../../../../intern/guardedalloc - ../../../../extern/fast_float ) set(INC_SYS @@ -19,11 +17,9 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc - intern/string_utils.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh - IO_string_utils.hh IO_types.h intern/dupli_parent_finder.hh ) @@ -42,7 +38,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/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/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..a7c4253f4d3 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -4,7 +4,6 @@ set(INC . ./exporter ./importer - ../common ../../blenkernel ../../blenlib ../../bmesh @@ -15,6 +14,7 @@ set(INC ../../makesrna ../../nodes ../../windowmanager + ../../../../extern/fast_float ../../../../extern/fmtlib/include ../../../../intern/guardedalloc ) @@ -35,6 +35,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,12 +51,12 @@ 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 ) set(LIB bf_blenkernel - bf_io_common ) if(WITH_TBB) @@ -69,8 +70,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/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index d14401224ed..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 { @@ -321,8 +320,8 @@ static bool parse_keyword(StringRef &line, StringRef keyword) if (!line.startswith(keyword)) { return false; } - /* Treat any ASCII control character as whitespace; don't use isspace() for performance reasons. - */ + /* Treat any ASCII control character as white-space; + * don't use `isspace()` for performance reasons. */ if (line[keyword_len] > ' ') { return false; } 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/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 3e7a4431bf5..1d5f1351de0 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -305,6 +305,12 @@ typedef enum eGP_Sculpt_Mode_Flag { GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2), /* apply brush to uv data */ GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3), + /* Stroke Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_STROKE = (1 << 4), + /* Layer Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER = (1 << 5), + /* Material Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL = (1 << 6), } eGP_Sculpt_Mode_Flag; typedef enum eAutomasking_flag { 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 bb53dbafdc8..e2ad5d38bbc 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -9,6 +9,8 @@ #include "DNA_ID.h" #include "DNA_customdata_types.h" +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -87,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; @@ -130,7 +135,13 @@ typedef struct Curves { /* Materials. */ struct Material **mat; short totcol; - short _pad2[3]; + + /** + * User-defined symmetry flag (#eCurvesSymmetryType) that causes editing operations to maintain + * symmetrical geometry. + */ + char symmetry; + char _pad2[5]; /** * Used as base mesh when curves represent e.g. hair or fur. This surface is used in edit modes. @@ -150,6 +161,14 @@ enum { HA_DS_EXPAND = (1 << 0), }; +/** #Curves.symmetry */ +typedef enum eCurvesSymmetryType { + CURVES_SYMMETRY_X = 1 << 0, + CURVES_SYMMETRY_Y = 1 << 1, + CURVES_SYMMETRY_Z = 1 << 2, +} eCurvesSymmetryType; +ENUM_OPERATORS(eCurvesSymmetryType, CURVES_SYMMETRY_Z) + /* Only one material supported currently. */ #define CURVES_MATERIAL_NR 1 diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index b0e7342c9cb..535533565dd 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -102,7 +102,8 @@ typedef struct NoiseGpencilModifierData { /** Noise Frequency scaling */ float noise_scale; float noise_offset; - char _pad[4]; + short noise_mode; + char _pad[2]; /** How many frames before recalculate randoms. */ int step; /** Custom index for passes. */ @@ -127,6 +128,11 @@ typedef enum eNoiseGpencil_Flag { GP_NOISE_INVERT_MATERIAL = (1 << 11), } eNoiseGpencil_Flag; +typedef enum eNoiseRandomGpencil_Mode { + GP_NOISE_RANDOM_STEP = 0, + GP_NOISE_RANDOM_KEYFRAME = 1, +} eNoiseRandomGpencil_Mode; + typedef struct SubdivGpencilModifierData { GpencilModifierData modifier; /** Material for filtering. */ 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_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index d5f7e25bb80..4ee5f34fcde 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -125,7 +125,7 @@ typedef struct ViewLayerAOV { int type; } ViewLayerAOV; -/* Lightgroup Renderpass definition. */ +/** Light-group Render-pass definition. */ typedef struct ViewLayerLightgroup { struct ViewLayerLightgroup *next, *prev; 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_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 77730ce254c..92a65a50bd4 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -632,7 +632,8 @@ .falloff = 4.0f, \ .mesh_verts_num = 0, \ .bind_verts_num = 0, \ - .polys_num = 0, \ + .target_verts_num = 0, \ + .target_polys_num = 0, \ .flags = 0, \ .mat = _DNA_DEFAULT_UNIT_M4, \ .strength = 1.0f, \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 73c4eeaaab3..6e3ce7e98a8 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2222,13 +2222,19 @@ typedef struct SurfaceDeformModifierData { struct Object *target; /** Vertex bind data. */ SDefVert *verts; + void *_pad1; float falloff; - unsigned int mesh_verts_num, bind_verts_num, polys_num; + /* Number of of vertices on the deformed mesh upon the bind process. */ + unsigned int mesh_verts_num; + /* Number of vertices in the `verts` array of this modifier. */ + unsigned int bind_verts_num; + /* Number of vertices and polygons on the target mesh upon bind process. */ + unsigned int target_verts_num, target_polys_num; int flags; float mat[4][4]; float strength; char defgrp_name[64]; - void *_pad1; + int _pad2; } SurfaceDeformModifierData; /** Surface Deform modifier flags. */ 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_object_types.h b/source/blender/makesdna/DNA_object_types.h index 9ea1e3a9e8d..f257833efe8 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -535,7 +535,8 @@ enum { /** Matches #OB_TYPE_SUPPORT_EDITMODE. */ #define OB_DATA_SUPPORT_EDITMODE(_type) \ - (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR, ID_CV)) + (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR) || \ + (U.experimental.use_new_curves_tools && (_type) == ID_CV)) /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ 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 3de6453bbaa..a8fa1fd4271 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; @@ -643,6 +643,8 @@ typedef struct UserDef_Experimental { /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ char use_new_curves_type; + /** Only available when #use_new_curves_type is enabled. */ + char use_new_curves_tools; char use_new_point_cloud_type; char use_full_frame_compositor; char use_sculpt_tools_tilt; @@ -650,7 +652,7 @@ typedef struct UserDef_Experimental { char use_override_templates; char enable_eevee_next; char use_sculpt_texture_paint; - char _pad0[2]; + char _pad0[1]; /** `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 86649357433..281aeae7a60 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -106,7 +106,7 @@ DNA_STRUCT_RENAME_ELEM(SDefBind, numverts, verts_num) DNA_STRUCT_RENAME_ELEM(SDefVert, numbinds, binds_num) DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type) DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, num_mesh_verts, mesh_verts_num) -DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, polys_num) +DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, target_polys_num) DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numverts, bind_verts_num) DNA_STRUCT_RENAME_ELEM(SurfaceModifierData, numverts, verts_num) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 74f8e2487a4..127c8465243 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -198,8 +198,10 @@ DEF_ENUM(rna_enum_context_mode_items) DEF_ENUM(rna_enum_preference_section_items) DEF_ENUM(rna_enum_attribute_type_items) +DEF_ENUM(rna_enum_color_attribute_type_items) DEF_ENUM(rna_enum_attribute_type_with_auto_items) DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_color_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) DEF_ENUM(rna_enum_geometry_component_type_items) diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index d49bac15242..2d9517ce1ee 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -40,12 +40,21 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_color_attribute_type_items[] = { + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"}, + {CD_PROP_BYTE_COLOR, + "BYTE_COLOR", + 0, + "Byte Color", + "RGBA color with 8-bit positive integer values"}, + {0, NULL, 0, NULL, NULL}}; + const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""}, {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, - {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with 32-bit floating-point values"}, {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, @@ -92,6 +101,11 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_color_attribute_domain_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}}; + #ifdef RNA_RUNTIME # include "BLI_math.h" diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 848779c49f7..f7edba24dea 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1844,6 +1844,26 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_automasking_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_STROKE); + RNA_def_property_ui_text(prop, "Auto-Masking Strokes", "Mask strokes below brush cursor"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_layer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_LAYER); + RNA_def_property_ui_text(prop, "Auto-Masking Layer", "Mask strokes using active layer"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_material", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL); + RNA_def_property_ui_text(prop, "Auto-Masking Material", "Mask strokes using active material"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* Material */ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Material"); @@ -1933,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, diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 7a1a368551f..2dc568d0b8a 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -274,6 +274,22 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Surface", "Mesh object that the curves can be attached to"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + /* Symmetry. */ + prop = RNA_def_property(srna, "use_mirror_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_X); + RNA_def_property_ui_text(prop, "X", "Enable symmetry in the X axis"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + + prop = RNA_def_property(srna, "use_mirror_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Y); + RNA_def_property_ui_text(prop, "Y", "Enable symmetry in the Y axis"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + + prop = RNA_def_property(srna, "use_mirror_z", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Z); + RNA_def_property_ui_text(prop, "Z", "Enable symmetry in the Z axis"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + /* attributes */ rna_def_attributes_common(srna); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 1f9bb972923..08986afe15e 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -232,6 +232,11 @@ static const EnumPropertyItem gpencil_envelope_mode_items[] = { "Add fill segments to create the envelope. Don't keep the original stroke"}, {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"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -959,6 +964,12 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + 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_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } @@ -3259,12 +3270,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_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index eaf2142495e..4cd12acd038 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; 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..11a1cf95d52 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -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 1a2017f2dbb..5ac324b3627 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1101,6 +1101,16 @@ int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char return GPU_mem_stats_supported() ? PROP_EDITABLE : 0; } +static int rna_userdef_experimental_use_new_curve_tools_editable(struct PointerRNA *UNUSED(ptr), + const char **r_info) +{ + if (U.experimental.use_new_curves_type) { + return PROP_EDITABLE; + } + *r_info = "Only available when new curves type is enabled"; + return 0; +} + #else # define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \ @@ -6394,6 +6404,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_type", 1); RNA_def_property_ui_text(prop, "New Curves Type", "Enable the new curves data type in the UI"); + prop = RNA_def_property(srna, "use_new_curves_tools", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_tools", 1); + RNA_def_property_editable_func(prop, "rna_userdef_experimental_use_new_curve_tools_editable"); + RNA_def_property_ui_text( + prop, "New Curves Tools", "Enable additional features for the new curves data block"); + prop = RNA_def_property(srna, "use_cycles_debug", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_cycles_debug", 1); RNA_def_property_ui_text(prop, "Cycles Debug", "Enable Cycles debugging options for developers"); 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/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index a80918b8d2b..8a0f49efb65 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1166,8 +1166,8 @@ static bool surfacedeformBind(Object *ob, SurfaceDeformModifierData *smd_eval, float (*vertexCos)[3], uint verts_num, - uint tpolys_num, - uint tverts_num, + uint target_polys_num, + uint target_verts_num, Mesh *target, Mesh *mesh) { @@ -1182,7 +1182,7 @@ static bool surfacedeformBind(Object *ob, SDefAdjacency *adj_array; SDefEdgePolys *edge_polys; - vert_edges = MEM_calloc_arrayN(tverts_num, sizeof(*vert_edges), "SDefVertEdgeMap"); + vert_edges = MEM_calloc_arrayN(target_verts_num, sizeof(*vert_edges), "SDefVertEdgeMap"); if (vert_edges == NULL) { BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Out of memory"); return false; @@ -1220,7 +1220,7 @@ static bool surfacedeformBind(Object *ob, } adj_result = buildAdjacencyMap( - mpoly, medge, mloop, tpolys_num, tedges_num, vert_edges, adj_array, edge_polys); + mpoly, medge, mloop, target_polys_num, tedges_num, vert_edges, adj_array, edge_polys); if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) { BKE_modifier_set_error( @@ -1233,7 +1233,8 @@ static bool surfacedeformBind(Object *ob, } smd_orig->mesh_verts_num = verts_num; - smd_orig->polys_num = tpolys_num; + smd_orig->target_verts_num = target_verts_num; + smd_orig->target_polys_num = target_polys_num; int defgrp_index; MDeformVert *dvert; @@ -1249,7 +1250,8 @@ static bool surfacedeformBind(Object *ob, .medge = medge, .mloop = mloop, .looptri = BKE_mesh_runtime_looptri_ensure(target), - .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetBindVertArray"), + .targetCos = MEM_malloc_arrayN( + target_verts_num, sizeof(float[3]), "SDefTargetBindVertArray"), .bind_verts = smd_orig->verts, .vertexCos = vertexCos, .falloff = smd_orig->falloff, @@ -1268,7 +1270,7 @@ static bool surfacedeformBind(Object *ob, invert_m4_m4(data.imat, smd_orig->mat); - for (int i = 0; i < tverts_num; i++) { + for (int i = 0; i < target_verts_num; i++) { mul_v3_m4v3(data.targetCos[i], smd_orig->mat, mvert[i].co); } @@ -1431,7 +1433,7 @@ static void surfacedeformModifier_do(ModifierData *md, { SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md; Mesh *target; - uint tverts_num, tpolys_num; + uint target_verts_num, target_polys_num; /* Exit function if bind flag is not set (free bind data if any). */ if (!(smd->flags & MOD_SDEF_BIND)) { @@ -1453,8 +1455,8 @@ static void surfacedeformModifier_do(ModifierData *md, return; } - tverts_num = BKE_mesh_wrapper_vert_len(target); - tpolys_num = BKE_mesh_wrapper_poly_len(target); + target_verts_num = BKE_mesh_wrapper_vert_len(target); + target_polys_num = BKE_mesh_wrapper_poly_len(target); /* If not bound, execute bind. */ if (smd->verts == NULL) { @@ -1473,25 +1475,62 @@ static void surfacedeformModifier_do(ModifierData *md, /* Avoid converting edit-mesh data, binding is an exception. */ BKE_mesh_wrapper_ensure_mdata(target); - if (!surfacedeformBind( - ob, smd_orig, smd, vertexCos, verts_num, tpolys_num, tverts_num, target, mesh)) { + if (!surfacedeformBind(ob, + smd_orig, + smd, + vertexCos, + verts_num, + target_polys_num, + target_verts_num, + target, + mesh)) { smd->flags &= ~MOD_SDEF_BIND; } /* Early abort, this is binding 'call', no need to perform whole evaluation. */ return; } - /* Poly count checks */ + /* Geometry count on the deforming mesh. */ if (smd->mesh_verts_num != verts_num) { BKE_modifier_set_error( ob, md, "Vertices changed from %u to %u", smd->mesh_verts_num, verts_num); return; } - if (smd->polys_num != tpolys_num) { + + /* Geometry count on the target mesh. */ + if (smd->target_polys_num != target_polys_num && smd->target_verts_num == 0) { + /* Change in the number of polygons does not really imply change in the vertex count, but + * this is how the modifier worked before the vertex count was known. Follow the legacy + * logic without requirement to re-bind the mesh. */ BKE_modifier_set_error( - ob, md, "Target polygons changed from %u to %u", smd->polys_num, tpolys_num); + ob, md, "Target polygons changed from %u to %u", smd->target_polys_num, target_polys_num); return; } + if (smd->target_verts_num != 0 && smd->target_verts_num != target_verts_num) { + if (smd->target_verts_num > target_verts_num) { + /* Number of vertices on the target did reduce. There is no usable recovery from this. */ + BKE_modifier_set_error(ob, + md, + "Target vertices changed from %u to %u", + smd->target_verts_num, + target_verts_num); + return; + } + + /* Assume the increase in the vertex count means that the "new" vertices in the target mesh are + * added after the original ones. This covers typical case when target was at the subdivision + * level 0 and then subdivision was increased (i.e. for the render purposes). */ + + BKE_modifier_set_error(ob, + md, + "Target vertices changed from %u to %u, continuing anyway", + smd->target_verts_num, + target_verts_num); + + /* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but + * it is not currently possible to request a subset of coordinates: the API expects that the + * caller needs coordinates of all vertices and asserts for it. */ + } /* Early out if modifier would not affect input at all - still *after* the sanity checks * (and potential binding) above. */ @@ -1507,7 +1546,7 @@ static void surfacedeformModifier_do(ModifierData *md, /* Actual vertex location update starts here */ SDefDeformData data = { .bind_verts = smd->verts, - .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetVertArray"), + .targetCos = MEM_malloc_arrayN(target_verts_num, sizeof(float[3]), "SDefTargetVertArray"), .vertexCos = vertexCos, .dvert = dvert, .defgrp_index = defgrp_index, @@ -1516,7 +1555,8 @@ static void surfacedeformModifier_do(ModifierData *md, }; if (data.targetCos != NULL) { - BKE_mesh_wrapper_vert_coords_copy_with_mat4(target, data.targetCos, tverts_num, smd->mat); + BKE_mesh_wrapper_vert_coords_copy_with_mat4( + target, data.targetCos, target_verts_num, smd->mat); TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); 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_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_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 7af3159bbf8..5b7211e44b4 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -81,14 +81,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_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index e0348f27e51..471d6af560f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -56,43 +56,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. @@ -435,8 +398,6 @@ static Curves *resample_to_uniform_count(const CurveComponent &src_component, /** * 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) @@ -574,7 +535,7 @@ static Field<int> get_curve_count_field(GeoNodeExecParams params, auto get_count_op = std::make_shared<FieldOperation>( FieldOperation(get_count_fn, - {Field<float>(std::make_shared<SplineLengthFieldInput>()), + {Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), params.extract_input<Field<float>>("Length")})); return Field<int>(std::move(get_count_op)); @@ -584,28 +545,6 @@ static Field<int> get_curve_count_field(GeoNodeExecParams params, 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,7 +552,7 @@ 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: 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 4118448b237..6e45411a9af 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 @@ -33,52 +33,49 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -template<class T> -static void scale_input_assign(const Span<T> input, +template<typename T> +static void scale_input_assign(const Span<T> src, const int scale, const int offset, - const MutableSpan<T> r_output) + MutableSpan<T> dst) { - for (const int i : IndexRange(r_output.size())) { - r_output[i] = input[i * scale + offset]; + for (const int i : dst.index_range()) { + dst[i] = src[i * scale + offset]; } } -template<class T> -static void scale_output_assign(const Span<T> input, +template<typename T> +static void scale_output_assign(const Span<T> src, const int scale, const int offset, - const MutableSpan<T> &r_output) + MutableSpan<T> dst) { - for (const int i : IndexRange(input.size())) { - r_output[i * scale + offset] = input[i]; + for (const int i : src.index_range()) { + dst[i * scale + offset] = src[i]; } } -template<class T> -static void nurbs_to_bezier_assign(const Span<T> input, - const MutableSpan<T> r_output, - const KnotsMode knotsMode) +template<typename T> +static void nurbs_to_bezier_assign(const Span<T> src, + const MutableSpan<T> dst, + const KnotsMode knots_mode) { - const int input_size = input.size(); - const int output_size = r_output.size(); - - switch (knotsMode) { + switch (knots_mode) { case NURBS_KNOT_MODE_BEZIER: - scale_input_assign<T>(input, 3, 1, r_output); + scale_input_assign<T>(src, 3, 1, dst); break; case NURBS_KNOT_MODE_NORMAL: - for (const int i : IndexRange(output_size)) { - r_output[i] = input[(i + 1) % input_size]; + for (const int i : dst.index_range()) { + dst[i] = src[(i + 1) % src.size()]; } break; case NURBS_KNOT_MODE_ENDPOINT_BEZIER: case NURBS_KNOT_MODE_ENDPOINT: - for (const int i : IndexRange(1, output_size - 2)) { - r_output[i] = input[i + 1]; + for (const int i : dst.index_range().drop_back(1).drop_front(1)) { + dst[i] = src[i + 1]; } - r_output.first() = input.first(); - r_output.last() = input.last(); + dst.first() = src.first(); + dst.last() = src.last(); break; } } @@ -357,7 +354,7 @@ static SplinePtr convert_to_nurbs(const Spline &input) static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSplineType &storage = node_storage(params.node()); - const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage.spline_type; + const GeometryNodeSplineType dst_type = (const GeometryNodeSplineType)storage.spline_type; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); @@ -386,7 +383,7 @@ static void node_geo_exec(GeoNodeExecParams params) threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) { for (const int i : range) { if (selection[i]) { - switch (output_type) { + switch (dst_type) { case GEO_NODE_SPLINE_TYPE_POLY: new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]); break; 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_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 1a0cc53cb6c..2ed6d555684 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, @@ -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_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 4832feac5bd..f2a859065c6 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_size(ATTR_DOMAIN_POINT); + const int edges_num = mesh_component.attribute_domain_size(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/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/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/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/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index a9b8d91ca03..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. */ @@ -1193,8 +1197,9 @@ typedef struct wmDropBox { struct wmDrag *drag, const int xy[2]); - /** Called with the draw buffer (#GPUViewport) set up for drawing into the region's view. - * \note Only setups the drawing buffer for drawing in view, not the GPU transform matricies. + /** + * Called with the draw buffer (#GPUViewport) set up for drawing into the region's view. + * \note Only setups the drawing buffer for drawing in view, not the GPU transform matrices. * The callback has to do that itself, with for example #UI_view2d_view_ortho. * \param xy: Cursor location in window coordinates (#wmEvent.xy compatible). */ 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) { |